<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	<meta name="theme-color" content="#33474d">
	<title>失落的乐章</title>
	<link rel="stylesheet" href="/css/style.css" />
	
      <link rel="alternate" href="/atom.xml" title="失落的乐章" type="application/atom+xml">
    
</head>

<body>

	<header class="header">
		<nav class="header__nav">
			
				<a href="/archives" class="header__link">Archive</a>
			
				<a href="/tags" class="header__link">Tags</a>
			
				<a href="/atom.xml" class="header__link">RSS</a>
			
		</nav>
		<h1 class="header__title"><a href="/">失落的乐章</a></h1>
		<h2 class="header__subtitle">技术面前，永远都是学生。</h2>
	</header>

	<main>
		
	<span class="different-posts different-posts_earlier">📖 <a href="/page/22">earlier posts</a> 📖</span>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/6. 概念了解：CGI，FastCGI，PHP-CGI与PHP-FPM/">概念了解：CGI，FastCGI，PHP-CGI与PHP-FPM</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/LNMP/">LNMP</a> <a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<h2 id="CGI"><a href="#CGI" class="headerlink" title="CGI"></a>CGI</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;CGI全称是“公共网关接口”(Common Gateway Interface)，HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具，其程序须运行在网络服务器上。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;CGI可以用任何一种语言编写，只要这种语言具有标准输入、输出和环境变量。如php,perl,tcl等。</p>
<h2 id="FastCGI"><a href="#FastCGI" class="headerlink" title="FastCGI"></a>FastCGI</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;FastCGI像是一个常驻(long-live)型的CGI，它可以一直执行着，只要激活后，不会每次都要花费时间去fork一次（这是CGI最为人诟病的fork-and-execute 模式）。它还支持分布式的运算，即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;FastCGI是语言无关的、可伸缩架构的CGI开放扩展，其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。众所周知，CGI解释器的反复加载是CGI性能低下的主要原因，如果CGI解释器保持在内存中并接受FastCGI进程管理器调度，则可以提供良好的性能、伸缩性、Fail- Over特性等等。</p>
<h3 id="FastCGI特点"><a href="#FastCGI特点" class="headerlink" title="FastCGI特点"></a>FastCGI特点</h3><ol>
<li>FastCGI具有语言无关性.<ol>
<li>FastCGI在进程中的应用程序，独立于核心web服务器运行，提供了一个比API更安全的环境。APIs把应用程序的代码与核心的web服务器链接在一起，这意味着在一个错误的API的应用程序可能会损坏其他应用程序或核心服务器。 恶意的API的应用程序代码甚至可以窃取另一个应用程序或核心服务器的密钥。</li>
<li>FastCGI技术目前支持语言有：C/C++、Java、Perl、Tcl、Python、SmallTalk、Ruby等。相关模块在Apache, ISS, Lighttpd等流行的服务器上也是可用的。</li>
<li>FastCGI的不依赖于任何Web服务器的内部架构，因此即使服务器技术的变化, FastCGI依然稳定不变。</li>
</ol>
</li>
</ol>
<h3 id="FastCGI的工作原理"><a href="#FastCGI的工作原理" class="headerlink" title="FastCGI的工作原理"></a>FastCGI的工作原理</h3><ol>
<li>Web Server启动时载入FastCGI进程管理器（IIS ISAPI或Apache Module)</li>
<li>FastCGI进程管理器自身初始化，启动多个CGI解释器进程(可见多个php-cgi)并等待来自Web Server的连接。</li>
<li>当客户端请求到达Web Server时，FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。</li>
<li>FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时，请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中，php-cgi在此便退出了。</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在上述情况中，你可以想象CGI通常有多慢。每一个Web请求PHP都必须重新解析php.ini、重新载入全部扩展并重初始化全部数据结构。使用FastCGI，所有这些都只在进程启动时发生一次。一个额外的好处是，持续数据库连接(Persistent database connection)可以工作。</p>
<h3 id="FastCGI的不足"><a href="#FastCGI的不足" class="headerlink" title="FastCGI的不足"></a>FastCGI的不足</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;因为是多进程，所以比CGI多线程消耗更多的服务器内存，PHP-CGI解释器每进程消耗7至25兆内存，将这个数字乘以50或100就是很大的内存数。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Nginx 0.8.46+PHP 5.2.14(FastCGI)服务器在3万并发连接下，开启的10个Nginx进程消耗150M内存（15M<em>10=150M），开启的64个php-cgi进程消耗1280M内存  （20M</em>64=1280M），加上系统自身消耗的内存，总共消耗不到2GB内存。如果服务器内存 较小，完全可以只开启25个php-cgi进程，这样php-cgi消耗的总内存数才500M。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;上面的数据摘自Nginx 0.8.x + PHP 5.2.13(FastCGI)搭建胜过Apache十倍的Web服务器(第6版)</p>
<h2 id="PHP-CGI"><a href="#PHP-CGI" class="headerlink" title="PHP-CGI"></a>PHP-CGI</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PHP-CGI是PHP自带的FastCGI管理器。</p>
<h3 id="PHP-CGI的不足："><a href="#PHP-CGI的不足：" class="headerlink" title="PHP-CGI的不足："></a>PHP-CGI的不足：</h3><ol>
<li>php-cgi变更php.ini配置后需重启php-cgi才能让新的php-ini生效，不可以平滑重启。</li>
<li>直接杀死php-cgi进程，php就不能运行了。(PHP-FPM和Spawn-FCGI就没有这个问题，守护进程会平滑从新生成新的子进程。）</li>
</ol>
<h2 id="PHP-FPM"><a href="#PHP-FPM" class="headerlink" title="PHP-FPM"></a>PHP-FPM</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PHP-FPM是一个PHP FastCGI管理器，是只用于PHP的，可以<a href="http://php-fpm.org/download" target="_blank" rel="external">下载</a>下载得到。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PHP-FPM其实是PHP源代码的一个补丁，旨在将FastCGI进程管理整合进PHP包中。必须将它patch到你的PHP源代码中，在编译安装PHP后才可以使用。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;现在我们可以在最新的PHP 5.3.2的源码树里下载得到直接整合了PHP-FPM的分支，据说下个版本会融合进PHP的主分支去。相对Spawn-FCGI，PHP-FPM在CPU和内存方面的控制都更胜一筹，而且前者很容易崩溃，必须用crontab进行监控，而PHP-FPM则没有这种烦恼。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PHP5.3.3已经集成php-fpm了，不再是第三方的包了。PHP-FPM提供了更好的PHP进程管理方式，可以有效控制内存和进程、可以平滑重载PHP配置，比spawn-fcgi具有更多有点，所以被PHP官方收录了。在./configure的时候带 –enable-fpm参数即可开启PHP-FPM。</p>
<h2 id="Spawn-FCGI"><a href="#Spawn-FCGI" class="headerlink" title="Spawn-FCGI"></a>Spawn-FCGI</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Spawn-FCGI是一个通用的FastCGI管理服务器，它是lighttpd中的一部份，很多人都用Lighttpd的Spawn-FCGI进行FastCGI模式下的管理工作，不过有不少缺点。而PHP-FPM的出现多少缓解了一些问题，但PHP-FPM有个缺点就是要重新编译，这对于一些已经运行的环境可能有不小的风险(refer)，在php 5.3.3中可以直接使用PHP-FPM了。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Spawn-FCGI目前已经独成为一个项目，更加稳定一些，也给很多Web 站点的配置带来便利。已经有不少站点将它与nginx搭配来解决动态网页。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;最新的<a href="http://www.lighttpd.net" target="_blank" rel="external">lighttpd</a>也没有包含这一块了，但可以在以前版本中找到它。在<a href="http://www.lighttpd.net/download/lighttpd-1.4.15.tar.gz" target="_blank" rel="external">lighttpd-1.4.15版本</a>中就包含了 ，<a href="http://redmine.lighttpd.net/projects/spawn-fcgi" target="_blank" rel="external">Spawn-FCGI的下 载地址</a>，<a href="http://www.lighttpd.net/download/spawn-fcgi-1.6.3.tar.gz" target="_blank" rel="external">最新版本Spawn-FCGI的下 载地址</a>。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;注：最新的Spawn-FCGI可以到lighttpd.net网站搜索“Spawn-FCGI”找到它的最新版本发布地址。</p>
<h2 id="PHP-FPM与spawn-CGI对比"><a href="#PHP-FPM与spawn-CGI对比" class="headerlink" title="PHP-FPM与spawn-CGI对比"></a>PHP-FPM与spawn-CGI对比</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PHP-FPM的使用非常方便，配置都是在PHP-FPM.ini的文件内，而启动、重启都可以从php/sbin/PHP-FPM中进行。更方便的是修改php.ini后可以直接使用PHP-FPM reload进行加载，无需杀掉进程就可以完成php.ini的修改加载</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;结果显示使用PHP-FPM可以使php有不小的性能提升。PHP-FPM控制的进程cpu回收的速度比较慢,内存分配的很均匀。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Spawn-FCGI控制的进程CPU下降的很快，而内存分配的比较不均匀。有很多进程似乎未分配到，而另外一些却占用很高。可能是由于进程任务分配的不均匀导致的。而这也导致了总体响应速度的下降。而PHP-FPM合理的分配，导致总体响应的提到以及任务的平均。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/5. mod_php和mod_fastcgi和php-fpm的介绍,对比,和性能数据/">mod_php和mod_fastcgi和php-fpm的介绍,对比,和性能数据</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/LNMP/">LNMP</a> <a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<h2 id="什么是mod-php和mod-fastcgi"><a href="#什么是mod-php和mod-fastcgi" class="headerlink" title="什么是mod_php和mod_fastcgi"></a>什么是mod_php和mod_fastcgi</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在lamp体系中，对于apache端php的配置，我们最常用的就是mod_php, 它把PHP做为APACHE一个内置模块。让apache http服务器本身能够支持PHP语言，不需要每一个请求就启动PHP解释器来解释PHP。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;和把webserver与php绑定起来的方式不同，fastcgi是HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具，相当于一个程序接口。它可以接受来自web服务器的请求，解释输入信息，将处理后的结果返回给服务器(apache,lighty等)。mod_fastcgi就是在apache下支持fastcgi协议的模块。</p>
<h2 id="工作原理"><a href="#工作原理" class="headerlink" title="工作原理"></a>工作原理</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在介绍这两种模式的工作原理前，我们先了解一下php的架构 </p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/01.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;上面是php的架构图，从图上可以看到， SAPI提供了一个和外部通信的接口，使得PHP可以和其他应用进行交互数据。php默认提供了很多种SAPI，常见的给apache的mod_php5，CGI，给IIS的ISAPI，还有Shell的CLI。对于一个基于apache的php应用，其运行流程可以简单归结如下 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>apache -&gt; httpd -&gt; mod_phpfastcgi -&gt; sapi -&gt; php</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;下面提到的两种工作方式就分别用到了mod_php5和cgi的sapi。  </p>
<h2 id="mod-php"><a href="#mod-php" class="headerlink" title="mod_php"></a>mod_php</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mod_php是在lamp体系中最常使用的工作方式，在这种模式下，php被编译为apache的一个内置模块，在启动时加载。当有一个php请求过来时，直接在httpd进程里完成php的解释运行，将结果返回。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在php的sapi中，有这样一个函数: sapi_cgibin_ub_write, 这个函数告诉了Zend，如何输出数据。查看mod_php的sapi源码，我们会发现，这个函数直接调用了apache的ap_rwrite函数。所以，用mod_php，我们可以把php和apache看做一个模块，两者绑定在一起。其工作原理如下图所示</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/02.png?raw=true" alt=""></figure></p>
<h2 id="mod-fastcgi"><a href="#mod-fastcgi" class="headerlink" title="mod_fastcgi"></a>mod_fastcgi</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在说明fastcgi之前，先了解一下普通cgi的工作流程： </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;web server收到用户请求，并把请求提交给cgi程序，cgi程序根据请求提交的参数作应处理，然后输出标准的html语句返回给web server，web server再返回给客户端，这就是普通cgi的工作原理。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;fastcgi是基于cgi架构的扩展，他的核心思想就是在web server和具体cgi程序之间建立一个智能的可持续的中间层，统管cgi程序的运行，这样web server只需要将请求提交给这个层，这个层再派生出几个可复用的cgi程序实例，然后再把请求分发给这些实例，这些实例是可控的，可持续，可复用的， 因此一方面避免了进程反复fork，另一方面又可以通过中间层的控制和探测机制来监视这些实例的运行情况，根据不同的状况fork或者回收实例，达到灵活 性和稳定性兼得的目的。modFastCGI的主要优点是把动态语言和web server分离开来。这种技术允许把web server和动态语言运行在不同的主机上，以大规模扩展和改进安全性而不损失生产效率。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;基于mod_fastcgi方式的php应用，其典型工作流程如下 </p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/03.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从图上可以看出， apache启动后，mod_fastcgi会在启动多个cgi程序，也就是php-cgi脚本。具体脚本的数目通过配置来指定。当有http请求到来后，httpd进程会选择一个当前空闲的一个php-cgi程序来执行，执行的方式和mod_php类似，也是通过php-cgi提供的sapi完成交互。查看源码，可以发现对于cgi的sapi，和mod_php不同，它是把结果输出到fastcgi提供的stdout上，fastcgi再将数据返回给httpd完成交互。 </p>
<h2 id="mod-factcgi的三种配置方式"><a href="#mod-factcgi的三种配置方式" class="headerlink" title="mod_factcgi的三种配置方式"></a>mod_factcgi的三种配置方式</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;基于mod_fastcgi的fastcgi应用一共有三种配置方式：静态、动态和远程。他们通过apache配置中的伪指令来代替，对应于三种方式的伪指令分别是FastCgiServer, FastCgiConfig, 以及FastCgiExternalServer。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;对于静态和动态这两种方式，apache将通过mod_fastcgi自带的进程管理工具(fcgi-pm)来管理fastcgi应用程序，也就是php-cgi。fcgi-pm在apache启动时就被自动激活了。相对于前两种，远程模式下，php-cgi不由fcgi-pm来管理，apache不会去关心php-cgi程序的状态。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;下面详细说一下三种配置方式 </p>
<h3 id="静态配置"><a href="#静态配置" class="headerlink" title="静态配置"></a>静态配置</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;通过FastCGIServer 伪指令将文件名定义为静态 FastCGI 应用程序。初始时需要指定启动的php-cgi进程数目，默认为1个。apache运行过程中，如果静态应用程序实例因为任何原因死了，那么fcgi-pm将衍生另一个实例来替换。 </p>
<h4 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">FastCgiServer file name [options]</div></pre></td></tr></table></figure>
<h4 id="重要参数说明"><a href="#重要参数说明" class="headerlink" title="重要参数说明"></a>重要参数说明</h4><ul>
<li><strong>idle-timeout n</strong>（30 秒）。在请求异常终止和事件记录在 error LogLevel 前，所允许的 FastCGI 应用程序不活动秒数。仅当存在与 FastCGI 应用程序的暂挂连接时，该不活动计时器才应用。如果应用程序在此期间不响应队列的请求，那么请求异常终止。如果与应用程序的通信完成，但是与客户机的通信未完成（缓存的响应），那么超时不应用。  </li>
<li><strong>processes n</strong>（1）。在服务器初始化时衍生的应用程序实例数。 </li>
<li><strong>port n</strong> 无。应用程序用于与 Web 服务器通信的 TCP 端口号（1-65535）。此选项使应用程序可以从网络中的其他机器访问。-socket 选项和 -port 选项是互斥的。 </li>
<li><strong>socket</strong> filename unix sock文件名  </li>
<li><strong>Restart-delay</strong> n（5 秒）。此应用程序的失败实例重新衍生之间的最小秒数。此延迟阻止中断的应用程序使用过多系统资源。 </li>
</ul>
<h3 id="动态配置"><a href="#动态配置" class="headerlink" title="动态配置"></a>动态配置</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在静态配置中，我们初始就指定了要启动的php进程数。和静态的相反，动态应用程序根据需求启动。php应用实例的数目根据当前http请求数目动态变化，进程的管理也是通过fcgi-pm来完成。</p>
<h4 id="语法-1"><a href="#语法-1" class="headerlink" title="语法"></a>语法</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">FastCgiConfig option [option ...]</div></pre></td></tr></table></figure>
<h4 id="重要参数说明-1"><a href="#重要参数说明-1" class="headerlink" title="重要参数说明"></a>重要参数说明</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;和静态方式相比，动态配置的参数主要增加了以下几个 </p>
<ul>
<li><strong>maxProcesses n</strong>  在任何时候允许运行的最大动态 FastCGI 应用程序实例数。 </li>
<li><strong>minProcesses n</strong> 任何时候允许运行，且无须由fcgi-pm（因没有需求）杀死的最小动态 FastCGI 应用程序实例数。  </li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在dyn模式下，因为fastcgi实例数目是动态改变的。所以没有静态方式中process这个参数 </p>
<h3 id="远程方式"><a href="#远程方式" class="headerlink" title="远程方式"></a>远程方式</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这种模式下，fastcgi实例和apache分离开来，这两者可以分开来部署。他们之间的通信通过tcp或者unix sock来完成。使用ext方式，fastcgi实例将不会由fcgi-pm来管理，而是独立的运行。 </p>
<h4 id="语法-2"><a href="#语法-2" class="headerlink" title="语法"></a>语法</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">FastCgiExternalServer 文件名 -host 主机名端口 [-appConnTimeout n] </div><div class="line">FastCgiExternalServer 文件名 -socket 文件名 [-appConnTimeout n]</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;重要参数说明 </p>
<ul>
<li><strong>idle-timeout n</strong> （30 秒） 在请求异常终止，并且事件记录之前，允许 FastCGI 应用程序保持不活动的秒数。只有当与 FastCGI 应用程序的连接暂挂时，此不活动定时器才适用。如果请求进入应用程序的请求队列，而此应用程序在此期间没有响应（通过写和刷新），则此请求将异常终止。如果与应用程序的通信已完成，而与客户机的通信尚未完成（响应被高速缓存），则此超时不适用。 </li>
<li><strong>host host:port</strong> 应用程序用于与 Web 服务器通信的主机名或 IP 地址和 TCP 端口号 (1-65535)。-socket 和 -host 选项是互斥的。  </li>
<li><strong>socket</strong> 文件名 无 </li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;应用程序用于与 Web 服务器通信的 UNIX 域套接字的文件名。此文件名相对于 FastCgiIpcDir。-socket 和 -port 选项是互斥的。 </p>
<h2 id="php-fpm"><a href="#php-fpm" class="headerlink" title="php-fpm"></a>php-fpm</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;使用FastCGI，最主要优点是把应用和web server(apache)分离开来。这样允许把web server和动态语言(php)运行在不同的主机上，以大规模扩展和改进安全性而不损失效率。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这样的情况下，对于php-cgi程序，由于从apache中分离出来，就需要一个单独的工具来对这些进程进行管理，在stc和dyn两种模式下，mod_fastcgi中自带的fcgi-pm会充当了这样的角色。可是，ext模式下却没有。我们只能通过一个脚本静态的启动n个实例，一旦进程死掉，还需要手工重启。当然，你可以使用supervise来管理这些进程，但是毕竟不那么灵活而且也仅仅一部份问题。甚至于fcgi-pm，因为实现的原因，其本身也有很多问题，例如不太稳定，压力下出core，无法平滑的完成切换等。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;基于上述的原因，我们需要一个稳定可靠的进程管理工具，就像lighty下的spwn-cgi。幸运的是，出现了php-fpm。它是一个类似于spwn-cgi的管理工具，可以和任何支持远端FastCGI的web server工作。在官方的手册上，列举了以下php-fpm所具有的特性：</p>
<table>
<thead>
<tr>
<th>特性</th>
<th>Php自带的</th>
<th>Spwn-cgi</th>
<th>Php-fpm</th>
</tr>
</thead>
<tbody>
<tr>
<td>php守护程序： pid file, log file, setsid(), setuid(), setgid(), chroot()</td>
<td>(-)</td>
<td>(-)</td>
<td>(+) </td>
</tr>
<tr>
<td>进程控制，可以平滑地重启、重新载入配置和二进制模块而不丢失请求</td>
<td>Php4 (-) Php5 只能平滑停止</td>
<td>(-)</td>
<td>(+)</td>
</tr>
<tr>
<td>限制ip地址，可以满足web server的要求</td>
<td>php4 (-) php5.2.2 &gt; (+)</td>
<td>(-)</td>
<td>(+)</td>
</tr>
<tr>
<td>如果使用优化器，在遇到opcode缓存随机损坏的时候紧急重启所有进程</td>
<td>(-)</td>
<td>(-)</td>
<td>(+)</td>
</tr>
<tr>
<td>使用用不同的uid / gid / chroot / 环境变量，不同的 php.ini 选项，不需要safe mode</td>
<td>(-)</td>
<td>(-)</td>
<td>(+)</td>
<td></td>
</tr>
<tr>
<td>记录work process的stdout和stderr</td>
<td>(-)</td>
<td>(-)</td>
<td>(+)</td>
</tr>
<tr>
<td>如果set_time_limit没有起作用，强制结束过期进程</td>
<td>(-)</td>
<td>(-)</td>
<td>(+)</td>
</tr>
</tbody>
</table>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;特色功能 Error header、优化的上传支持、fastcgi_finish_request() 另外，php-fpm还可以兼容zend Optimize，各种缓存优化器。</p>
<h2 id="Php-fpm的安装"><a href="#Php-fpm的安装" class="headerlink" title="Php-fpm的安装"></a>Php-fpm的安装</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;php-fpm是以patch的方式安装的，如果要使用它，必须在安装php前打上这个补丁。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;eg:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">gzip -<span class="built_in">cd</span> php-5.2.6-fpm-0.5.9.diff.gz | patch -d php-5.2.6 -p1</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在configure时加上–enable-fpm选项。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;安装完php后，会有以下几个文件</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="variable">$prefix</span>/ext/php-fpm.conf </div><div class="line"><span class="variable">$prefix</span>/<span class="built_in">log</span>/php-fpm.log </div><div class="line"><span class="variable">$prefix</span>/<span class="built_in">log</span>/php-fpm.pid</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;其中php-fpm.conf是配置文件，具体如何配置文件里有非常详尽的注释。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;最后，执行./bin/php-cgi –fpm &amp;就可以了。  </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;另外，你也可以使用$prexif/sbin/php-fpm脚本来方便的维护。</p>
<h2 id="性能测试"><a href="#性能测试" class="headerlink" title="性能测试"></a>性能测试</h2><h3 id="名称解释"><a href="#名称解释" class="headerlink" title="名称解释"></a>名称解释</h3><ul>
<li><strong>ab</strong>  apache自测的性能测试工具，主要用于测试极限压力：对于同一url每秒所能执行的次数及响应时间。</li>
<li><strong>myab</strong>  baidu开发的压力测试工具，区别于ab，其更主要的功能是测试指定压力条件下机器的负载情况。</li>
<li><strong>eacc</strong>  一种php加速器，主要是将php程序编译后的结果缓存起来。加速php的执行，对性能有很大幅度的提升 ，更详细的情况可以参见另一篇关于php缓存优化的文档 《缓存优化工具-php加速的利器》</li>
<li><strong>etc、stc、dyn</strong>  mod_fastcgi运行的模式，分别指远程、静态和动态模式    </li>
</ul>
<h3 id="测试内容"><a href="#测试内容" class="headerlink" title="测试内容"></a>测试内容</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;测试中我们将针对不同的运行模式和页面类型进行极限压力测试及机器负载测试。同时，我们还将测试使用缓存优化和不使用缓存优化下机器的表现情况。另外，由于我们的测试主要是对比两种模式的性能差别，因此对于机器的硬件配置不是特别敏感，测试的时候只需要保证机器负载为0即可。</p>
<h3 id="测试结果"><a href="#测试结果" class="headerlink" title="测试结果"></a>测试结果</h3><ul>
<li>最简单的php页面<br>压力工具：mysql<br>压力情况：每秒1000次请求，并发数70。<br>性能指标：cpu idle     </li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;不同模式下机器idle情况如下图</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/04.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从图上我们可以看出，测试最简单的php页面时，各种模式下性能几乎没有差别，且由于页面非常简单，eacc的效果也基本没有。</p>
<ul>
<li>复杂的php程序（2000行代码+）<br>压力工具：mysql<br>压力情况：每秒400次请求,并发数70<br>性能指标：cpu idle    </li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;测试结果如下图</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/05.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从图上可以看出，在php代码复杂的情况下，前面4中模式性能差别不大，mod_php略优，然后fastcgi动态配置的方式在此情况下性能差距非常之大，idle直接降为0，同时虽然每秒是400次的压力，其只能处理200次左右的请求。 由于php代码很复杂，eacc的作用明显体现出来，使用缓存优化后性能提升一倍以上。  </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;另外，还可以看出，eacc这样的缓存优化工具对于mod_php和fastcgi方式起到的作用基本相同，他们之间的性能差异和使用前维持相同。  在上述情况下，如果不适应fpm，fastcgi模式会出core，主要原因还在在于进程管理的问题。</p>
<ul>
<li>实际项目中的php程序<br>这里我们采用群组项目中的pb页面，涉及到和数据库交互，db交互2次。<br>db部署在其他机器，每次请求耗时50ms左右。<br>压力工具：mysql<br>压力情况：每秒400次请求,并发数70<br>性能指标：cpu idle     </li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;测试结果如下</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/06.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从图上可以看出，性能的情况和图2类似，各种模式的差距仍然不大。 </p>
<ul>
<li>极限压力测试<br>压力工具：ab<br>测试参数：并发请求数100，总次数10000次。<br>测试页面：pb页面<br>性能指标：每秒请求数 rps </li>
</ul>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/07.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从上图我们可以看到，极限压力下，使用eacc后，mod_php和ext(fpm)基本差不多，保持在860/s左右的水平，而使用fpm方式进行管理的模式略优于其他两种fastcgi模式。  </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;测试中发现，fastcgi模式下，不使用php-fpm管理的话，仍然会有core出现。   </p>
<h2 id="fastcgi配置项对性能的影响。"><a href="#fastcgi配置项对性能的影响。" class="headerlink" title="fastcgi配置项对性能的影响。"></a>fastcgi配置项对性能的影响。</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在mod_fastcgi下，不管是ext还是stc方式，有一个配置项是非常关键的：默认启动的php-cgi进程实例数 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从fastcgi原理可以看出，当有一个httpd进程到来时，它需要调用fastcgi server来执行，如果此时所有server都出于服务状态，则这个httpd进程将出于等待状态得不到服务，导致请求无法响应。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;对pb页面进行测试，配置不同的实例数目进行极限压力测试，得到结果如下</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/08.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;分析一下这个原因：   </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;我们的pb页面执行时间是50ms，也就是说一个php-cgi程序在1s内可以处理的请求数目是1000/50 = 20。假设启动的实例数目是n，则1s内最多能完成的请求数为n<em>20，从图上可以看出，实际结果符合我们的计算。另外，这个数值也不是无限增大了，当n</em>20大于server本身所能承受的极限时，rps也不会继续往上增长了。   </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;所以，使用fastcgi，对于n的选择，要综合页面平均处理时间，最大压力等多种因素结合来配置。     </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;同时，在ext方式下，webserver和fastcgi server交互的方式有两种：通过tcp或者unix sock。测试一下这两种方式的情况 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;使用myab每秒400次请求pb页面，机器idle </p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/mod_php%E5%92%8Cmod_fastcgi%E5%92%8Cphp-fpm%E7%9A%84%E4%BB%8B%E7%BB%8D,%E5%AF%B9%E6%AF%94,%E5%92%8C%E6%80%A7%E8%83%BD%E6%95%B0%E6%8D%AE/09.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从图上可以看出，不管是否使用eacc，tcp方式的idle都有一个百分点的下降，这个差异主要是因为多了tcp连接过程造成的。   </p>
<h2 id="分析-amp-结论"><a href="#分析-amp-结论" class="headerlink" title="分析&amp;结论"></a>分析&amp;结论</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;根据各种测试结果，可以看出和fastcgi方式相比，mod_php的性能在各种情况下都稍优，这种差异主要是在于后一种方式增加了一次数据交互过程php-&gt;fascgi-&gt;apache。但是这个差距并不大，在使用了eacc等缓存优化工具后，性能有了很大提升。他们之间的差距完全不是瓶颈。从使用的角度来说，fastcgi具有以下优点：<br>1． webserver和php程序分离，两者可以部署在不同的地方，通过socket方式通信带来一定安全性<br>2． 使用fastcgi，在出现问题时可以更好的定位是webserver还是php的原因<br>3． fastcgi方式不依赖于webserver，更加灵活，扩展性也更好<br>4． fastcgi本身会有一些进程监控和日志记录，更便于分析问题，跟踪状态。<br>5． 灵活多样的配置，可以根据实际的应用进行合理配置达到最佳效果。   </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当然fastcgi也有一些缺点<br>1． mod_fastcgi在进程管理上有一些问题，容易出core。这个问题通过使用php-fpm可以解决<br>2． 由于fastcgi应用单独分离出来，因此需要单独监控进程的状态。防止进程挂掉后导致服务出现问题，这个可以通过使用用supervise管理一定程度上避免这个问题。<br>3． 文档相对缺乏，mod_fastcgi对apache的支持也不是特别好，且基本没有升级。   </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;关于fastcgi运行方式的选择，从效率、稳定性等各方面来说，ext方式是最佳选择了。而且考虑到我们可能会将webserver和php分开到不同机器，选择远程方式也是必须的。  </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;进程管理工具，从各方面来说，php-fpm是最优选择了，即时使用lighty作用web server，也完全可以用它代替spwn-cgi。   </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;综合测试结果和上述分析，我们完全可以采用fastcgi代替传统的mod_php。推荐使用下面的组合方式</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;apache + ext + php-fpm(with superwise)</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果webserver和fastcgi部署在同一机器上，使用unix sock方式通信，否则使用tcp方式。</p>
<h2 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h2><h3 id="apache-mod-fastcgi-php搭建"><a href="#apache-mod-fastcgi-php搭建" class="headerlink" title="apache+mod_fastcgi+php搭建"></a>apache+mod_fastcgi+php搭建</h3><ul>
<li>php<br>mod_fastcgi的搭建主要有三种方式<br>stc、ext和dyn，不管是哪种方式，首先在安装php的时候需要加上如下选项<br>–enable-fastcgi，并且不能使用—with-apxs。<br>以下是一个配置php例子 </li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">./configure \</div><div class="line">--prefix=/home/club/hongdk/env/php5.26-fcgi/ \</div><div class="line">--<span class="built_in">enable</span>-trace-vars \ </div><div class="line">--with-zlib-dir=/home/club/hongdk/tool/zlib/ \</div><div class="line">--with-mysql=/home/club/hongdk/env/mysql5/ \  </div><div class="line">--with-mysqli=/home/club/hongdk/env/mysql5/bin/mysql_config \</div><div class="line">--with-gettext \</div><div class="line">--with-iconv \ </div><div class="line">--<span class="built_in">enable</span>-mbstring=gbk \</div><div class="line">--with-xmlrpc \  </div><div class="line">--<span class="built_in">enable</span>-safe-mode \</div><div class="line">--<span class="built_in">enable</span>-sockets \</div><div class="line">--<span class="built_in">enable</span>-url-fopen-wrapper \</div><div class="line">--<span class="built_in">enable</span>-ftp \</div><div class="line">--<span class="built_in">enable</span>-shmop \</div><div class="line">--with-config-file-path=/home/club/hongdk/env/php5.26-fcgi/ \</div><div class="line">--<span class="built_in">enable</span>-xml \</div><div class="line">--with-dom=/home/club/hongdk/tool/libxml/ \</div><div class="line">--with-libxml-dir=/home/club/hongdk/tool/libxml/ \ </div><div class="line">--with-curl=/home/club/hongdk/tool/curl \</div><div class="line">--with-curlwrappers \</div><div class="line">--<span class="built_in">enable</span>-fastcgi \</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在安装完后，会在php的bin目录下找到如下文件php-cgi</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;执行 <code>./php-cgi –v</code>，如果看到</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">PHP 5.2.5 (cgi-fcgi) (built: Nov 12 2008 20:44:08)</div><div class="line">Copyright (c) 1997-2007 The PHP Group </div><div class="line">Zend Engine v2.2.0, Copyright (c) 1998-2007 Zend Technologies</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;就表明安装成功了</p>
<ul>
<li>mod_fastcgi </li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">cd</span> mod_fastcgi-2.4.6 </div><div class="line">cp Makefile.AP2 Makefile </div><div class="line">vi Makefile</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;修改top_dir为apache安装目录</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">make</div><div class="line">make install</div></pre></td></tr></table></figure>
<ul>
<li>apache配置<br>修改httpd.conf<br>首先增加 </li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">LoadModule fastcgi_module modules/mod_fastcgi.so</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;然后根据不同的运行模式进行配置</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;eg </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ext模式</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&lt;IfModule mod_fastcgi.c&gt; </div><div class="line">    FastCgiExternalServer <span class="variable">$php</span>-server -socket /home/club/fastcgi.sock</div><div class="line">    ScriptAlias /cgi-bin/ <span class="string">"/home/club/hongdk/env/apache-fcgi/cgi-bin/"</span></div><div class="line">    AddHandler php-fastcgi .php </div><div class="line">    Action php-fastcgi /cgi-bin/php-cgi</div><div class="line">    AddType application/x-httpd-php .php</div><div class="line">&lt;/IfModule&gt;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;其中 <code>$php-sever</code> 为php-cgi程序,例如/home/club/hongdk/env/apache-fcgi/cgi-bin/php-cgi</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;stc模式</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&lt;IfModule mod_fastcgi.c&gt;</div><div class="line">    FastCgiServer <span class="variable">$php</span>-server -processes 10 </div><div class="line">    ScriptAlias /cgi-bin/ <span class="string">"/home/club/hongdk/env/apache-fcgi/cgi-bin/"</span></div><div class="line">    AddHandler php-fastcgi .php </div><div class="line">    Action php-fastcgi /cgi-bin/php-cgi</div><div class="line">    AddType application/x-httpd-php .php</div><div class="line">&lt;/IfModule&gt; </div><div class="line">`</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;dyn模式</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&lt;IfModule mod_fastcgi.c&gt;</div><div class="line">    FastcgiConfig –idle-timeout 120 </div><div class="line">    ScriptAlias /cgi-bin/ <span class="string">"/home/club/hongdk/env/apache-fcgi/cgi-bin/"</span></div><div class="line">    AddHandler php-fastcgi .php</div><div class="line">    Action php-fastcgi /cgi-bin/php-cgi </div><div class="line">    AddType application/x-httpd-php .php</div><div class="line">&lt;/IfModule&gt;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;然后，定义fastcgi server也就是我们的php-cgi程序所在文件夹的权限</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&lt;Directory <span class="variable">$php</span>-server&gt;</div><div class="line">    Options ExecCGI</div><div class="line">    Order allow,deny</div><div class="line">    Allow from all</div><div class="line">&lt;/Directory&gt;</div></pre></td></tr></table></figure>
<ul>
<li>fastcgi应用<br>在我们的应用中就是php-cgi<br>如果是stc和dyn方式，不需要单独再启动php-cgi，他们的管理有mod_fastcgi自带的fcgi-pm来完成<br>在etc模式下，我们需要单独启动php-cgi程序<br>可以用php-fpm来完成。也可以独立的使用一个脚本来启动，例如</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#!/bin/sh</span></div><div class="line"><span class="built_in">exec</span> &amp;&gt; /dev/null</div><div class="line"><span class="built_in">exec</span> &lt; /dev/null</div><div class="line">PHP_FCGI_CHILDREN=32</div><div class="line"><span class="built_in">export</span> PHP_FCGI_CHILDREN</div><div class="line"><span class="built_in">exec</span> /home/club/hongdk/env/apache-fcgi/cgi-bin/php-cgi -b ./xl.sock -c /home/club/hongdk/env/php5-fcgi/php.ini</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;推荐使用php-fpm来完成</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/4. Nginx为什么比Apache Httpd高效：原理篇/">Nginx为什么比Apache Httpd高效：原理篇</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/LNMP/">LNMP</a> <a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Nginx才短短几年，就拿下了web服务器大笔江山，众所周知，Nginx在处理大并发静态请求方面，效率明显高于httpd，甚至能轻松解决C10K问题。下面我们就来聊聊Web服务器背后的一些原理。</p>
<h2 id="一、进程、线程？"><a href="#一、进程、线程？" class="headerlink" title="一、进程、线程？"></a>一、进程、线程？</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;进程是具有一定独立功能的，在计算机中已经运行的程序的实体。在早期系统中（如linux 2.4以前），进程是基本运作单位，在支持线程的系统中（如windows，linux2.6）中，线程才是基本的运作单位，而进程只是线程的容器。程序本身只是指令、数据及其组织形式的描述，进程才是程序（那些指令和数据）的真正运行实例。若干进程有可能与同一个程序相关系，且每个进程皆可以同步（循序）或异步（平行）的方式独立运行。现代计算机系统可在同一段时间内以进程的形式将多个程序加载到存储器中，并借由时间共享（或称时分复用），以在一个处理器上表现出同时（平行性）运行的感觉。同样的，使用多线程技术（多线程即每一个线程都代表一个进程内的一个独立执行上下文）的操作系统或计算机架构，同样程序的平行线程，可在多 CPU 主机或网络上真正同时运行（在不同的CPU上）。</p>
<h2 id="二、常见Web服务方式"><a href="#二、常见Web服务方式" class="headerlink" title="二、常见Web服务方式"></a>二、常见Web服务方式</h2><h3 id="三种工作模型比较："><a href="#三种工作模型比较：" class="headerlink" title="三种工作模型比较："></a>三种工作模型比较：</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Web服务器要为用户提供服务，必须以某种方式，工作在某个套接字上。一般Web服务器在处理用户请求是，一般有如下三种方式可选择：多进程方式、多线程方式、异步方式。</p>
<ul>
<li>多进程方式：为每个请求启动一个进程来处理。由于在操作系统中，生成进程、销毁进程、进程间切换都很消耗CPU和内存，当负载高是，性能会明显降低。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>优点： 稳定性！</strong>由于采用独立进程处理独立请求，而进程之间是独立的，单个进程问题不会影响其他进程，因此稳定性最好。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>缺点： 资源占用！</strong>当请求过大时，需要大量的进程处理请求，进程生成、切换开销很大，而且进程间资源是独立的，造成内存重复利用。</p>
<ul>
<li>多线程方式：一个进程中用多个线程处理用户请求。由于线程开销明显小于进程，而且部分资源还可以共享，因此效率较高。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>优点：开销较小！</strong>线程间部分数据是共享的，且线程生成与线程间的切换所需资源开销比进程间切换小得多。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>缺点：稳定性！</strong>线程切换过快可能造成线程抖动，且线程过多会造成服务器不稳定。</p>
<ul>
<li>异步方式：使用非阻塞方式处理请求，是三种方式中开销最小的。但异步方式虽然效率高，但要求也高，因为多任务之间的调度如果出现问题，就可能出现整体故障，因此使用异步工作的，一般是一些功能相对简单，但却符合服务器任务调度、且代码中没有影响调度的错误代码存在的程序。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>优点：性能最好！</strong>一个进程或线程处理多个请求，不需要额外开销，性能最好，资源占用最低。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>缺点：稳定性！</strong>某个进程或线程出错，可能导致大量请求无法处理，甚至导致整个服务宕机。</p>
<h3 id="一个Web请求的处理过程："><a href="#一个Web请求的处理过程：" class="headerlink" title="一个Web请求的处理过程："></a>一个Web请求的处理过程：</h3><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/01.png?raw=true" alt=""></figure></p>
<ol>
<li>客户发起情况到服务器网卡；</li>
<li>服务器网卡接受到请求后转交给内核处理；</li>
<li>内核根据请求对应的套接字，将请求交给工作在用户空间的Web服务器进程</li>
<li>Web服务器进程根据用户请求，向内核进行系统调用，申请获取相应资源（如index.html）</li>
<li>内核发现web服务器进程请求的是一个存放在硬盘上的资源，因此通过驱动程序连接磁盘</li>
<li>内核调度磁盘，获取需要的资源</li>
<li>内核将资源存放在自己的缓冲区中，并通知Web服务器进程</li>
<li>Web服务器进程通过系统调用取得资源，并将其复制到进程自己的缓冲区中</li>
<li>Web服务器进程形成响应，通过系统调用再次发给内核以响应用户请求</li>
<li>内核将响应发送至网卡</li>
<li>网卡发送响应给用户</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;通过这样的一个复杂过程，一次请求就完成了。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;简单来说就是：<strong>用户请求–&gt;送达到用户空间–&gt;系统调用–&gt;内核空间–&gt;内核到磁盘上读取网页资源-&gt;返回到用户空间-&gt;响应给用户</strong>。上述简单的说明了一下，客户端向Web服务请求过程，在这个过程中，有两个I/O过程，一个就是客户端请求的网络I/O，另一个就是Web服务器请求页面的磁盘I/O。 下面我们就来说说Linux的I/O模型。</p>
<h2 id="三、各种I-O模型详解"><a href="#三、各种I-O模型详解" class="headerlink" title="三、各种I/O模型详解"></a>三、各种I/O模型详解</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;通过上面的对连接的处理分析，我们知道工作在用户空间的web服务器进程是无法直接操作IO的，需要通过系统调用进行，其关系如下：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/02.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;即进程向内核进行系统调用申请IO，内核将资源从IO调度到内核的buffer中（<code>wait阶段</code>），内核还需将数据从内核buffer中复制（<code>copy阶段</code>）到web服务器进程所在的用户空间，才算完成一次IO调度。这几个阶段都是需要时间的。根据wait和copy阶段的处理等待的机制不同，可将I/O动作分为如下五种模式：</p>
<ul>
<li>阻塞I/O</li>
<li>非阻塞I/O</li>
<li>I/O复用（select和poll）</li>
<li>信号（事件）驱动I/O（SIGIO）</li>
<li>异步I/O（aio）</li>
</ul>
<h3 id="I-O模型简介"><a href="#I-O模型简介" class="headerlink" title="I/O模型简介"></a>I/O模型简介</h3><pre><code>这里有必要先解释一下**阻塞、非阻塞，同步、异步、I/O**的概念。
</code></pre><h4 id="阻塞和非阻塞"><a href="#阻塞和非阻塞" class="headerlink" title="阻塞和非阻塞:"></a>阻塞和非阻塞:</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;阻塞和非阻塞指的是执行一个操作是等操作结束再返回，还是马上返回。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;比如餐馆的服务员为用户点菜，当有用户点完菜后，服务员将菜单给后台厨师，此时有两种方式：</p>
<ul>
<li>第一种：就在出菜窗口等待，直到厨师炒完菜后将菜送到窗口，然后服务员再将菜送到用户手中；</li>
<li>第二种：等一会再到窗口来问厨师，某个菜好了没？如果没有先处理其他事情，等会再去问一次；</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第一种就是阻塞方式，第二种则是非阻塞的。</p>
<h4 id="同步和异步"><a href="#同步和异步" class="headerlink" title="同步和异步:"></a>同步和异步:</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;同步和异步又是另外一个概念，它是事件本身的一个属性。还拿前面点菜为例，服务员直接跟厨师打交道，菜出来没出来，服务员直接指导，但只有当厨师将菜送到服务员手上，这个过程才算正常完成，这就是同步的事件。同样是点菜，有些餐馆有专门的传菜人员，当厨师炒好菜后，传菜员将菜送到传菜窗口，并通知服务员，这就变成异步的了。其实异步还可以分为两种：带通知的和不带通知的。前面说的那种属于带通知的。有些传菜员干活可能主动性不是很够，不会主动通知你，你就需要时不时的去关注一下状态。这种就是不带通知的异步。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;对于同步的事件，你只能以阻塞的方式去做。而对于异步的事件，阻塞和非阻塞都是可以的。非阻塞又有两种方式：主动查询和被动接收消息。被动不意味着一定不好，在这里它恰恰是效率更高的，因为在主动查询里绝大部分的查询是在做无用功。对于带通知的异步事件，两者皆可。而对于不带通知的，则只能用主动查询。</p>
<h4 id="全异步I-O"><a href="#全异步I-O" class="headerlink" title="全异步I/O"></a>全异步I/O</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;回到I/O，不管是I还是O，对外设(磁盘)的访问都可以分成请求和执行两个阶段。请求就是看外设的状态信息（比如是否准备好了），执行才是真正的I/O操作。在Linux 2.6之前，只有“请求”是异步事件，2.6之后才引入AIO（asynchronous I/O ）把“执行”异步化。别看Linux/Unix是用来做服务器的，这点上比Windows落后了好多，IOCP（Windows上的AIO，效率极高）在Win2000上就有了。所以学linux的别老觉得Windows这里不好那里不好（Windows的多线程机制也由于linux）。</p>
<h4 id="I-O的五种模型"><a href="#I-O的五种模型" class="headerlink" title="I/O的五种模型"></a>I/O的五种模型</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;根据以上分析，I/O可分为五种模型：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/03.png?raw=true" alt=""></figure></p>
<ul>
<li><strong>阻塞I/O</strong>：所有过程全阻塞</li>
<li><strong>非阻塞I/O</strong>：如果没有数据buffer，则立即返回EWOULDBLOCK</li>
<li><strong>I/O复用（select和poll）</strong>：在wait和copy阶段分别阻塞</li>
<li><strong>信号驱动I/O（SIGIO）</strong>：在wait阶段不阻塞，但copy阶段阻塞（信号驱动I/O，即通知）</li>
<li><strong>异步I/O（aio）</strong>：完全五阻塞方式，当I/O完成是提供信号</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Linux上的前四种I/O模型的“执行”阶段都是同步的，只有最后一种才做到了真正的全异步。第一种阻塞式是最原始的方法，也是最累的办法。当然累与不累要看针对谁。应用程序是和内核打交道的。对应用程序来说，这种方式是最累的，但对内核来说这种方式恰恰是最省事的。还拿点菜这事为例，你就是应用程序，厨师就是内核，如果你去了一直等着，厨师就省事了（不用同时处理其他服务员的菜）。当然现在计算机的设计，包括操作系统，越来越为终端用户考虑了，为了让用户满意，内核慢慢的承担起越来越多的工作，IO模型的演化也是如此。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;非阻塞I/O ，I/O复用，信号驱动式I/O其实都是非阻塞的，当然是针对“请求”这个阶段。非阻塞式是主动查询外设状态。I/O复用里的select，poll也是主动查询，不同的是select和poll可以同时查询多个fd（文件句柄）的状态，另外select有fd个数的限制。epoll是基于回调函数的。信号驱动式I/O则是基于信号消息的。这两个应该可以归到“被动接收消息”那一类中。最后就是伟大的AIO的出现，内核把什么事都干了，对上层应用实现了全异步，性能最好，当然复杂度也最高。</p>
<h3 id="各I-O模型详细介绍："><a href="#各I-O模型详细介绍：" class="headerlink" title="各I/O模型详细介绍："></a>各I/O模型详细介绍：</h3><h4 id="阻塞I-O"><a href="#阻塞I-O" class="headerlink" title="阻塞I/O"></a>阻塞I/O</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;说明：应用程序调用一个IO函数，导致应用程序阻塞，等待数据准备好。 如果数据没有准备好，一直等待数据准备好了，从内核拷贝到用户空间,IO函数返回成功指示。这个不用多解释吧，阻塞套接字。下图是它调用过程的图示：（注，一般网络I/O都是阻塞I/O，客户端发出请求，Web服务器进程响应，在进程没有返回页面之前，这个请求会处于一直等待状态）</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/04.png?raw=true" alt=""></figure></p>
<h4 id="非阻塞I-O"><a href="#非阻塞I-O" class="headerlink" title="非阻塞I/O"></a>非阻塞I/O</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;我们把一个套接口设置为非阻塞就是告诉内核，当所请求的I/O操作无法完成时，不要将进程睡眠，而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好，如果没有准备好，继续测试，直到数据准备好为止。在这个不断测试的过程中，会大量的占用CPU的时间，所有一般Web服务器都不使用这种I/O模型。具体过程如下图:</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/05.png?raw=true" alt=""></figure></p>
<h4 id="I-O复用（select和poll）"><a href="#I-O复用（select和poll）" class="headerlink" title="I/O复用（select和poll）"></a>I/O复用（select和poll）</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;I/O复用模型会用到select或poll函数或epoll函数(Linux2.6以后的内核开始支持)，这两个函数也会使进程阻塞，但是和阻塞I/O所不同的的，这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作，多个写操作的I/O函数进行检测，直到有数据可读或可写时，才真正调用I/O操作函数。具体过程如下图:</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/06.png?raw=true" alt=""></figure></p>
<h4 id="信号驱动I-O（SIGIO）"><a href="#信号驱动I-O（SIGIO）" class="headerlink" title="信号驱动I/O（SIGIO）"></a>信号驱动I/O（SIGIO）</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;首先，我们允许套接口进行信号驱动I/O，并安装一个信号处理函数，进程继续运行并不阻塞。当数据准备好时，进程会收到一个SIGIO信号，可以在信号处理函数中调用I/O操作函数处理数据。具体过程如下图:</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/07.jpg?raw=true" alt=""></figure></p>
<h4 id="异步I-O（aio）"><a href="#异步I-O（aio）" class="headerlink" title="异步I/O（aio）"></a>异步I/O（aio）</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当一个异步过程调用发出后，调用者不能立刻得到结果。实际处理这个调用的部件在完成后，通过状态、通知和回调来通知调用者的输入输出操作。具体过程如下图:</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/08.png?raw=true" alt=""></figure></p>
<h4 id="模型总结（如下图）"><a href="#模型总结（如下图）" class="headerlink" title="模型总结（如下图）"></a>模型总结（如下图）</h4><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%94Apache%20Httpd%E9%AB%98%E6%95%88%EF%BC%9A%E5%8E%9F%E7%90%86%E7%AF%87/09.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从上图中我们可以看出，可以看出，越往后，阻塞越少，理论上效率也是最优。其五种I/O模型中，前三种属于同步I/O，后两者属于异步I/O。</p>
<p><strong>同步I/O</strong>:</p>
<ul>
<li>阻塞I/O</li>
<li>非阻塞I/O</li>
<li>I/O复用（select和poll）</li>
</ul>
<p><strong>异步I/O</strong>:</p>
<ul>
<li>信号驱动I/O（SIGIO） （半异步）</li>
<li>异步I/O（aio） （真正的异步）</li>
</ul>
<p><strong>异步 I/O 和 信号驱动I/O的区别</strong>:</p>
<ul>
<li>信号驱动 I/O 模式下，内核可以复制的时候通知给我们的应用程序发送SIGIO 消息。</li>
<li>异步 I/O 模式下，内核在所有的操作都已经被内核操作结束之后才会通知我们的应用程序。</li>
</ul>
<h3 id="Linux-I-O模型的具体实现"><a href="#Linux-I-O模型的具体实现" class="headerlink" title="Linux I/O模型的具体实现"></a>Linux I/O模型的具体实现</h3><h4 id="主要实现方式有以下几种："><a href="#主要实现方式有以下几种：" class="headerlink" title="主要实现方式有以下几种："></a>主要实现方式有以下几种：</h4><ul>
<li>select</li>
<li>poll</li>
<li>epoll</li>
<li>kqueue</li>
<li>/dev/poll</li>
<li>iocp</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;注，其中iocp是Windows实现的，select、poll、epoll是Linux实现的，kqueue是FreeBSD实现的，/dev/poll是SUN的Solaris实现的。select、poll对应第3种（I/O复用）模型，iocp对应第5种（异步I/O）模型，那么epoll、kqueue、/dev/poll呢？其实也同select属于同一种模型，只是更高级一些，可以看作有了第4种（信号驱动I/O）模型的某些特性，如callback机制。</p>
<h4 id="为什么epoll、kqueue、-dev-poll比select高级？"><a href="#为什么epoll、kqueue、-dev-poll比select高级？" class="headerlink" title="为什么epoll、kqueue、/dev/poll比select高级？"></a>为什么epoll、kqueue、/dev/poll比select高级？</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;答案是，他们无轮询。因为他们用callback取代了。想想看，当套接字比较多的时候，每次select()都要通过遍历FD_SETSIZE个Socket来完成调度，不管哪个Socket是活跃的，都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数，当他们活跃时，自动完成相关操作，那就避免了轮询，这正是epoll、kqueue、/dev/poll做的。这样子说可能不好理解，那么我说一个现实中的例子，假设你在大学读书，住的宿舍楼有很多间房间，你的朋友要来找你。select版宿管大妈就会带着你的朋友挨个房间去找，直到找到你为止。而epoll版宿管大妈会先记下每位同学的房间号，你的朋友来时，只需告诉你的朋友你住在哪个房间即可，不用亲自带着你的朋友满大楼找人。如果来了10000个人，都要找自己住这栋楼的同学时，select版和epoll版宿管大妈，谁的效率更高，不言自明。同理，在高并发服务器中，轮询I/O是最耗时间的操作之一，select、epoll、/dev/poll的性能谁的性能更高，同样十分明了。</p>
<h4 id="Windows-or-nix-（IOCP-or-kqueue、epoll、-dev-poll）？"><a href="#Windows-or-nix-（IOCP-or-kqueue、epoll、-dev-poll）？" class="headerlink" title="Windows or *nix （IOCP or kqueue、epoll、/dev/poll）？"></a>Windows or *nix （IOCP or kqueue、epoll、/dev/poll）？</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;诚然，Windows的IOCP非常出色，目前很少有支持asynchronous I/O的系统，但是由于其系统本身的局限性，大型服务器还是在UNIX下。而且正如上面所述，kqueue、epoll、/dev/poll 与 IOCP相比，就是多了一层从内核copy数据到应用层的阻塞，从而不能算作asynchronous I/O类。但是，这层小小的阻塞无足轻重，kqueue、epoll、/dev/poll 已经做得很优秀了。</p>
<h4 id="总结一些重点"><a href="#总结一些重点" class="headerlink" title="总结一些重点"></a>总结一些重点</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;只有IOCP(windows实现)是asynchronous I/O，其他机制或多或少都会有一点阻塞。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;select（Linux实现）低效是因为每次它都需要轮询。但低效也是相对的，视情况而定，也可通过良好的设计改善</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;epoll(Linux实现)、kqueue（FreeBSD实现）、/dev/poll（Solaris实现）是Reacor模式，IOCP是Proactor模式。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Apache 2.2.9之前只支持select模型，2.2.9之后支持epoll模型</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Nginx 支持epoll模型</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Java nio包是select模型</p>
<h2 id="四、Apache-Httpd的工作模式"><a href="#四、Apache-Httpd的工作模式" class="headerlink" title="四、Apache Httpd的工作模式"></a>四、Apache Httpd的工作模式</h2><h3 id="apache三种工作模式"><a href="#apache三种工作模式" class="headerlink" title="apache三种工作模式"></a>apache三种工作模式</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;我们都知道Apache有三种工作模块，分别为prefork、worker、event。</p>
<ul>
<li>prefork：多进程，每个请求用一个进程响应，这个过程会用到select机制来通知。</li>
<li>worker：多线程，一个进程可以生成多个线程，每个线程响应一个请求，但通知机制还是select不过可以接受更多的请求。</li>
<li>event：基于异步I/O模型，一个进程或线程，每个进程或线程响应多个用户请求，它是基于事件驱动（也就是epoll机制）实现的。</li>
</ul>
<h3 id="prefork的工作原理"><a href="#prefork的工作原理" class="headerlink" title="prefork的工作原理"></a>prefork的工作原理</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果不用“–with-mpm”显式指定某种MPM,prefork就是Unix平台上缺省的MPM.它所采用的预派生子进程方式也是 Apache1.3中采用的模式。prefork本身并没有使用到线程，2.0版使用它是为了与1.3版保持兼容性；另一方面，prefork用单独的子进程来处理不同的请求，进程之间是彼此独立的,这也使其成为最稳定的MPM之一。</p>
<h3 id="worker的工作原理"><a href="#worker的工作原理" class="headerlink" title="worker的工作原理"></a>worker的工作原理</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;相对于prefork，worker是2.0版中全新的支持多线程和多进程混合模型的MPM。由于使用线程来处理，所以可以处理相对海量的请求，而系统资源的开销要小于基于进程的服务器。但是，worker也使用了多进程,每个进程又生成多个线程，以获得基于进程服务器的稳定性，这种MPM的工作方 式将是Apache2.0的发展趋势。</p>
<h3 id="event-基于事件机制的特性"><a href="#event-基于事件机制的特性" class="headerlink" title="event 基于事件机制的特性"></a>event 基于事件机制的特性</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;一个进程响应多个用户请求，利用callback机制，让套接字复用，请求过来后进程并不处理请求，而是直接交由其他机制来处理，通过epoll机制来通知请求是否完成；在这个过程中，进程本身一直处于空闲状态，可以一直接收用户请求。可以实现一个进程程响应多个用户请求。支持持海量并发连接数，消耗更少的资源。</p>
<h2 id="五、如何提高Web服务器的并发连接处理能力"><a href="#五、如何提高Web服务器的并发连接处理能力" class="headerlink" title="五、如何提高Web服务器的并发连接处理能力"></a>五、如何提高Web服务器的并发连接处理能力</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;有几个基本条件：</p>
<ul>
<li>基于线程，即一个进程生成多个线程，每个线程响应用户的每个请求。</li>
<li>基于事件的模型，一个进程处理多个请求，并且通过epoll机制来通知用户请求完成。</li>
<li>基于磁盘的AIO（异步I/O）</li>
<li>支持mmap内存映射，mmap传统的web服务器，进行页面输入时，都是将磁盘的页面先输入到内核缓存中，再由内核缓存中复制一份到web服务器上，mmap机制就是让内核缓存与磁盘进行映射，web服务器，直接复制页面内容即可。不需要先把磁盘的上的页面先输入到内核缓存去。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;刚好，Nginx 支持以上所有特性。所以Nginx官网上说，Nginx支持50000并发，是有依据的。</p>
<h2 id="六、Nginx优异之处"><a href="#六、Nginx优异之处" class="headerlink" title="六、Nginx优异之处"></a>六、Nginx优异之处</h2><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;传统上基于进程或线程模型架构的web服务通过每进程或每线程处理并发连接请求，这势必会在网络和I/O操作时产生阻塞，其另一个必然结果则是对内存或CPU的利用率低下。生成一个新的进程/线程需要事先备好其运行时环境，这包括为其分配堆内存和栈内存，以及为其创建新的执行上下文等。这些操作都需要占用CPU，而且过多的进程/线程还会带来线程抖动或频繁的上下文切换，系统性能也会由此进一步下降。另一种高性能web服务器/web服务器反向代理：Nginx（Engine X），nginx的主要着眼点就是其高性能以及对物理计算资源的高密度利用，因此其采用了不同的架构模型。受启发于多种操作系统设计中基于“事件”的高级处理机制，nginx采用了模块化、事件驱动、异步、单线程及非阻塞的架构，并大量采用了多路复用及事件通知机制。在nginx中，连接请求由为数不多的几个仅包含一个线程的进程worker以高效的回环(run-loop)机制进行处理，而每个worker可以并行处理数千个的并发连接及请求。</p>
<h3 id="Nginx-工作原理"><a href="#Nginx-工作原理" class="headerlink" title="Nginx 工作原理"></a>Nginx 工作原理</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Nginx会按需同时运行多个进程：一个主进程(master)和几个工作进程(worker)，配置了缓存时还会有缓存加载器进程(cache loader)和缓存管理器进程(cache manager)等。所有进程均是仅含有一个线程，并主要通过“共享内存”的机制实现进程间通信。主进程以root用户身份运行，而worker、cache loader和cache manager均应以非特权用户身份运行。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;主进程主要完成如下工作：</p>
<ul>
<li>读取并验正配置信息；</li>
<li>创建、绑定及关闭套接字；</li>
<li>启动、终止及维护worker进程的个数；</li>
<li>无须中止服务而重新配置工作特性；</li>
<li>控制非中断式程序升级，启用新的二进制程序并在需要时回滚至老版本；</li>
<li>重新打开日志文件；</li>
<li>编译嵌入式perl脚本；</li>
<li>worker进程主要完成的任务包括：</li>
<li>接收、传入并处理来自客户端的连接；</li>
<li>提供反向代理及过滤功能；</li>
<li>nginx任何能完成的其它任务；</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;注：如果负载以CPU密集型应用为主，如SSL或压缩应用，则worker数应与CPU数相同；如果负载以IO密集型为主，如响应大量内容给客户端，则worker数应该为CPU个数的1.5或2倍。</p>
<h3 id="Nginx-架构"><a href="#Nginx-架构" class="headerlink" title="Nginx 架构"></a>Nginx 架构</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Nginx的代码是由一个核心和一系列的模块组成, 核心主要用于提供Web Server的基本功能，以及Web和Mail反向代理的功能；还用于启用网络协议，创建必要的运行时环境以及确保不同的模块之间平滑地进行交互。不过，大多跟协议相关的功能和某应用特有的功能都是由nginx的模块实现的。这些功能模块大致可以分为事件模块、阶段性处理器、输出过滤器、变量处理器、协议、upstream和负载均衡几个类别，这些共同组成了nginx的http功能。事件模块主要用于提供OS独立的(不同操作系统的事件机制有所不同)事件通知机制如kqueue或epoll等。协议模块则负责实现nginx通过http、tls/ssl、smtp、pop3以及imap与对应的客户端建立会话。在Nginx内部，进程间的通信是通过模块的pipeline或chain实现的；换句话说，每一个功能或操作都由一个模块来实现。例如，压缩、通过FastCGI或uwsgi协议与upstream服务器通信，以及与memcached建立会话等。</p>
<h3 id="Nginx-基础功能"><a href="#Nginx-基础功能" class="headerlink" title="Nginx 基础功能"></a>Nginx 基础功能</h3><ul>
<li>处理静态文件，索引文件以及自动索引；</li>
<li>反向代理加速(无缓存)，简单的负载均衡和容错；</li>
<li>FastCGI，简单的负载均衡和容错；</li>
<li>模块化的结构。过滤器包括gzipping, byte ranges, chunked responses, 以及 SSI-filter 。在SSI过滤器中，到同一个 proxy 或者 FastCGI 的多个子请求并发处理；</li>
<li>SSL 和 TLS SNI 支持；</li>
</ul>
<h3 id="Nginx-IMAP-POP3-代理服务功能"><a href="#Nginx-IMAP-POP3-代理服务功能" class="headerlink" title="Nginx IMAP/POP3 代理服务功能"></a>Nginx IMAP/POP3 代理服务功能</h3><ul>
<li>使用外部 HTTP 认证服务器重定向用户到 IMAP/POP3 后端；</li>
<li>使用外部 HTTP 认证服务器认证用户后连接重定向到内部的 SMTP 后端；</li>
<li>认证方法：</li>
<li>POP3: POP3 USER/PASS, APOP, AUTH LOGIN PLAIN CRAM-MD5;</li>
<li>IMAP: IMAP LOGIN;</li>
<li>SMTP: AUTH LOGIN PLAIN CRAM-MD5;</li>
<li>SSL 支持；</li>
<li>在 IMAP 和 POP3 模式下的 STARTTLS 和 STLS 支持；</li>
</ul>
<h3 id="Nginx-支持的操作系统"><a href="#Nginx-支持的操作系统" class="headerlink" title="Nginx 支持的操作系统"></a>Nginx 支持的操作系统</h3><ul>
<li>FreeBSD 3.x, 4.x, 5.x, 6.x i386; FreeBSD 5.x, 6.x amd64;</li>
<li>Linux 2.2, 2.4, 2.6 i386; Linux 2.6 amd64;</li>
<li>Solaris 8 i386; Solaris 9 i386 and sun4u; Solaris 10 i386;</li>
<li>MacOS X (10.4) PPC;</li>
<li>Windows 编译版本支持 windows 系列操作系统;</li>
</ul>
<h3 id="Nginx-结构与扩展"><a href="#Nginx-结构与扩展" class="headerlink" title="Nginx 结构与扩展"></a>Nginx 结构与扩展</h3><ul>
<li>一个主进程和多个工作进程，工作进程运行于非特权用户；</li>
<li>kqueue (FreeBSD 4.1+), epoll (Linux 2.6+), rt signals (Linux 2.2.19+), /dev/poll (Solaris 7 11/99+), select, 以及 poll 支持；</li>
<li>kqueue支持的不同功能包括 EV_CLEAR, EV_DISABLE （临时禁止事件）， NOTE_LOWAT, EV_EOF, 有效数据的数目，错误代码；</li>
<li>sendfile (FreeBSD 3.1+), sendfile (Linux 2.2+), sendfile64 (Linux 2.4.21+), 和 sendfilev (Solaris 8 7/01+) 支持；</li>
<li>输入过滤 (FreeBSD 4.1+) 以及 TCP_DEFER_ACCEPT (Linux 2.4+) 支持；</li>
<li>10,000 非活动的 HTTP keep-alive 连接仅需要 2.5M 内存。</li>
<li>最小化的数据拷贝操作；</li>
</ul>
<h3 id="Nginx-其他HTTP功能"><a href="#Nginx-其他HTTP功能" class="headerlink" title="Nginx 其他HTTP功能"></a>Nginx 其他HTTP功能</h3><ul>
<li>基于IP 和名称的虚拟主机服务；</li>
<li>Memcached 的 GET 接口；</li>
<li>支持 keep-alive 和管道连接；</li>
<li>灵活简单的配置；</li>
<li>重新配置和在线升级而无须中断客户的工作进程；</li>
<li>可定制的访问日志，日志写入缓存，以及快捷的日志回卷；</li>
<li>4xx-5xx 错误代码重定向；</li>
<li>基于 PCRE 的 rewrite 重写模块；</li>
<li>基于客户端 IP 地址和 HTTP 基本认证的访问控制；</li>
<li>PUT, DELETE, 和 MKCOL 方法；</li>
<li>支持 FLV （Flash 视频）；</li>
<li>带宽限制；</li>
</ul>
<h3 id="为什么选择Nginx"><a href="#为什么选择Nginx" class="headerlink" title="为什么选择Nginx"></a>为什么选择Nginx</h3><ul>
<li>在高连接并发的情况下，Nginx是Apache服务器不错的替代品: Nginx在美国是做虚拟主机生意的老板们经常选择的软件平台之一. 能够支持高达 50,000 个并发连接数的响应, 感谢Nginx为我们选择了 epoll and kqueue 作为开发模型。</li>
<li>Nginx作为负载均衡服务器: Nginx 既可以在内部直接支持 Rails 和 PHP 程序对外进行服务, 也可以支持作为 HTTP代理 服务器对外进行服务. Nginx采用C进行编写, 不论是系统资源开销还是CPU使用效率都比 Perlbal 要好很多。</li>
<li>作为邮件代理服务器: Nginx 同时也是一个非常优秀的邮件代理服务器（最早开发这个产品的目的之一也是作为邮件代理服务器）, Last.fm 描述了成功并且美妙的使用经验.</li>
<li>Nginx 安装非常的简单 , 配置文件非常简洁（还能够支持perl语法）,Bugs 非常少的服务器: Nginx 启动特别容易, 并且几乎可以做到7*24不间断运行，即使运行数个月也不需要重新启动. 你还能够 不间断服务的情况下进行软件版本的升级 。</li>
<li>Nginx 的诞生主要解决C10K问题</li>
</ul>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/37. nginx的location优先级/">nginx的location优先级</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在nginx配置文件中，location主要有这几种形式：</p>
<ol>
<li><p>正则匹配  location ~ /abc { }</p>
</li>
<li><p>不区分大小写的正则匹配 location ~* /abc { }</p>
</li>
<li><p>匹配路径的前缀，如果找到停止搜索 location ^~ /abc { }</p>
</li>
<li><p>精确匹配  location = /abc { }</p>
</li>
</ol>
<p>5.普通路径前缀匹配  location  /abc { }</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;先说优先级</p>
<p>4  &gt; 3  &gt; 2  &gt; 1 &gt;  5</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;再来解释一下各个格式</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div></pre></td><td class="code"><pre><div class="line">location  = / &#123;</div><div class="line">  <span class="comment"># 精确匹配 / ，主机名后面不能带任何字符串</span></div><div class="line">  [ configuration A ] </div><div class="line">&#125;</div><div class="line"></div><div class="line">location  / &#123;</div><div class="line">  <span class="comment"># 因为所有的地址都以 / 开头，所以这条规则将匹配到所有请求</span></div><div class="line">  <span class="comment"># 但是正则和最长字符串会优先匹配</span></div><div class="line">  [ configuration B ] </div><div class="line">&#125;</div><div class="line"></div><div class="line">location /documents/ &#123;  </div><div class="line">  <span class="comment"># 匹配任何以 /documents/ 开头的地址，匹配符合以后，还要继续往下搜索</span></div><div class="line">  <span class="comment"># 只有后面的正则表达式没有匹配到时，这一条才会采用这一条</span></div><div class="line">  [ configuration C ] </div><div class="line">&#125;</div><div class="line"></div><div class="line">location ~ /documents/Abc &#123;  </div><div class="line">  <span class="comment"># 匹配任何以 /documents/ 开头的地址，匹配符合以后，还要继续往下搜索</span></div><div class="line">  <span class="comment"># 只有后面的正则表达式没有匹配到时，这一条才会采用这一条</span></div><div class="line">  [ configuration CC ] </div><div class="line">&#125;</div><div class="line"></div><div class="line">location ^~ /images/ &#123;  </div><div class="line">  <span class="comment"># 匹配任何以 /images/ 开头的地址，匹配符合以后，停止往下搜索正则，采用这一条。</span></div><div class="line">  [ configuration D ] </div><div class="line">&#125;</div><div class="line"></div><div class="line">location ~* \.(gif|jpg|jpeg)$ &#123;  </div><div class="line">  <span class="comment"># 匹配所有以 gif,jpg或jpeg 结尾的请求</span></div><div class="line">  <span class="comment"># 然而，所有请求 /images/ 下的图片会被 config D 处理，因为 ^~ 到达不了这一条正则</span></div><div class="line">  [ configuration E ] </div><div class="line">&#125;</div><div class="line"></div><div class="line">location /images/ &#123;  </div><div class="line">  <span class="comment"># 字符匹配到 /images/，继续往下，会发现 ^~ 存在</span></div><div class="line">  [ configuration F ] </div><div class="line">&#125;</div><div class="line"></div><div class="line">location /images/abc &#123;  </div><div class="line">  <span class="comment"># 最长字符匹配到 /images/abc，继续往下，会发现 ^~ 存在</span></div><div class="line">  <span class="comment"># F与G的放置顺序是没有关系的</span></div><div class="line">  [ configuration G ] </div><div class="line">&#125;</div><div class="line"></div><div class="line">location ~ /images/abc/ &#123;  </div><div class="line">  <span class="comment"># 只有去掉 config D 才有效：先最长匹配 config G 开头的地址，继续往下搜索，匹配到这一条正则，采用</span></div><div class="line">    [ configuration H ] </div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;再来分析一下A-H配置的执行顺序。</p>
<ol>
<li>下面2个配置同时存在时</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">location = / &#123;</div><div class="line">[ configuration A ]</div><div class="line">&#125;</div><div class="line"></div><div class="line">location / &#123;</div><div class="line">[ configuration B ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;此时A生效，因为=/优先级高于/</p>
<ol>
<li>下面3个配置同时存在时</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">location  /documents/ &#123;</div><div class="line">[ configuration C ]</div><div class="line">&#125;</div><div class="line"></div><div class="line">location ~ /documents/ &#123;</div><div class="line"></div><div class="line">[configuration CB]</div><div class="line"></div><div class="line">&#125;</div><div class="line"></div><div class="line">location ~ /documents/abc &#123;</div><div class="line">[ configuration CC ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当访问的url为/documents/abc/1.html，此时CC生效，首先CB优先级高于C，而CC更优先于CB</p>
<ol>
<li>下面4个配置同时存在时</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">location ^~ /images/ &#123;</div><div class="line">[ configuration D ]</div><div class="line">&#125;</div><div class="line"></div><div class="line">location /images/ &#123;</div><div class="line">[ configuration F ]</div><div class="line">&#125;</div><div class="line"></div><div class="line">location /images/abc &#123;</div><div class="line">[ configuration G ]</div><div class="line">&#125;</div><div class="line"></div><div class="line">location ~ /images/abc/ &#123;</div><div class="line">[ configuration H ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当访问的链接为/images/abc/123.jpg时，此时D生效。虽然4个规则都能匹配到，但^~优先级是最高的。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;若^~不存在时，H优先，因为~/images/ &gt; /images/</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;而/images/和/images/abc同时存在时，/images/abc优先级更高，因为后者更加精准</p>
<ol>
<li>下面两个配置同时存在时</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">location ~* \.(gif|jpg|jpeg)$ &#123;</div><div class="line">[ configuration E ]</div><div class="line">&#125;</div><div class="line"></div><div class="line">location ~ /images/abc/ &#123;</div><div class="line"></div><div class="line">[ configuration H ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当访问的链接为/images/abc/123.jpg时，E生效。因为上面的规则更加精准。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/35. Nginx负载均衡算法分析/">Nginx负载均衡算法分析</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;随着互联网信息的爆炸性增长，负载均衡（load balance）已经不再是一个很陌生的话题，顾名思义，负载均衡即是将负载分摊到不同的服务单元，既保证服务的可用性，又保证响应足够快，给用户很好的体验。快速增长的访问量和数据流量催生了各式各样的负载均衡产品，很多专业的负载均衡硬件提供了很好的功能，但却价格不菲（如F5 BIG-IP、Citrix NetScaler、Radware等等，虽然可以解决问题，但其高昂的价格却往往令人望而却步），这使得负载均衡软件大受欢迎，nginx就是其中的一个。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;nginx第一个公开版本发布于2004年，2011年发布了1.0版本。它的特点是稳定性高、功能强大、资源消耗低，从其目前的市场占有而言，nginx大有与apache抢市场的势头。其中不得不提到的一个特性就是其负载均衡功能，这也成了很多公司选择它的主要原因。本文将从源码的角度介绍nginx的内置负载均衡策略和扩展负载均衡策略，以实际的工业生产为案例，对比各负载均衡策略，为nginx使用者提供参考。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;nginx的负载均衡策略可以划分为两大类：内置策略和扩展策略。内置策略包含加权轮询和ip hash，在默认情况下这两种策略会编译进nginx内核，只需在nginx配置中指明参数即可。扩展策略有很多，如fair、通用hash、consistent hash等，默认不编译进nginx内核。由于在nginx版本升级中负载均衡的代码没有本质性的变化，因此下面将以nginx1.0.15稳定版为例，从源码角度分析各个策略。</p>
<h2 id="1-加权轮询（weighted-round-robin）"><a href="#1-加权轮询（weighted-round-robin）" class="headerlink" title="1. 加权轮询（weighted round robin）"></a>1. 加权轮询（weighted round robin）</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;轮询的原理很简单，首先我们介绍一下轮询的基本流程。如下是处理一次请求的流程图：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/01.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;图中有两点需要注意，第一，如果可以把加权轮询算法分为先深搜索和先广搜索，那么nginx采用的是先深搜索算法，即将首先将请求都分给高权重的机器，直到该机器的权值降到了比其他机器低，才开始将请求分给下一个高权重的机器；第二，当所有后端机器都down掉时，nginx会立即将所有机器的标志位清成初始状态，以避免造成所有的机器都处在timeout的状态，从而导致整个前端被夯住。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;接下来看下源码。nginx源码的目录结构很清晰，加权轮询所在路径为nginx-1.0.15/src/http/ngx_http_upstream_round_robin.[c|h]，在源码的基础上，针对重要的、不易理解的地方我加了注释。首先看下ngx_http_upstream_round_robin.h中的重要声明：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/02.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从变量命名中，我们就可以大致猜出其作用。其中，current_weight和weight的区别主要是前者为权重排序的值，随着处理请求会动态的变化，后者是配置值，用于恢复初始状态。<br>接下来看下轮询的创建过程，代码如下图所示。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/03.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这里有个tried变量需要做些说明。tried中记录了服务器当前是否被尝试连接过。他是一个位图。如果服务器数量小于32，则只需在一个int中即可记录下所有服务器状态。如果服务器数量大于32，则需在内存池中申请内存来存储。对该位图数组的使用可参考如下代码：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/04.png?raw=true" alt=""></figure></p>
<p>最后是实际的策略代码，逻辑很简单，代码实现也只有30行，直接上代码。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/05.png?raw=true" alt=""></figure></p>
<h2 id="2-ip-hash"><a href="#2-ip-hash" class="headerlink" title="2. ip hash"></a>2. ip hash</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ip hash是nginx内置的另一个负载均衡的策略，流程和轮询很类似，只是其中的算法和具体的策略有些变化，如下图所示：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/06.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ip hash算法的核心实现如下图：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/07.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从代码中可以看出，hash值既与ip有关又与后端机器的数量有关。经过测试，上述算法可以连续产生1045个互异的value，这是该算法的硬限制。对此nginx使用了保护机制，当经过20次hash仍然找不到可用的机器时，算法退化成轮询。因此，从本质上说，ip hash算法是一种变相的轮询算法，如果两个ip的初始hash值恰好相同，那么来自这两个ip的请求将永远落在同一台服务器上，这为均衡性埋下了很深的隐患。</p>
<h2 id="3-fair"><a href="#3-fair" class="headerlink" title="3. fair"></a>3. fair</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;fair策略是扩展策略，默认不被编译进nginx内核。其原理是根据后端服务器的响应时间判断负载情况，从中选出负载最轻的机器进行分流。这种策略具有很强的自适应性，但是实际的网络环境往往不是那么简单，因此要慎用。</p>
<h2 id="4-通用hash、一致性hash"><a href="#4-通用hash、一致性hash" class="headerlink" title="4. 通用hash、一致性hash"></a>4. 通用hash、一致性hash</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这两种也是扩展策略，在具体的实现上有些差别，通用hash比较简单，可以以nginx内置的变量为key进行hash，一致性hash采用了nginx内置的一致性hash环，可以支持memcache。<br>对上面的集中负载均衡算法进行测试（测试工具polygraph），考察下面三个关键的测试指标：</p>
<ul>
<li>均衡性：是否能够将请求均匀的发送给后端</li>
<li>一致性：同一个key的请求，是否能落到同一台机器</li>
<li>容灾性：当部分后端机器挂掉时，是否能够正常工作</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;通过实际的对比测试，我们对nginx各个负载均衡策略进行了验证。下面从均衡性、一致性、容灾性以及适用场景等角度对比各种策略。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/08.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;无论哪种策略都不是万金油，在具体的场景下应该选择哪种策略一定程度上依赖于使用者对这些策略的熟悉程度。希望本文的分析和测试数据能够对读者有所帮助，更希望有越来越多、越来越好的负载均衡策略产出。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="http://wiki.nginx.org/HttpUpstreamConsistentHash" target="_blank" rel="external">http://wiki.nginx.org/HttpUpstreamConsistentHash</a></p>
<p><a href="http://wiki.nginx.org/HttpUpstreamFairModule" target="_blank" rel="external">http://wiki.nginx.org/HttpUpstreamFairModule</a></p>
<p><a href="http://wiki.nginx.org/HttpUpstreamRequestHashModule" target="_blank" rel="external">http://wiki.nginx.org/HttpUpstreamRequestHashModule</a></p>
<p><a href="http://www.web-polygraph.org/" target="_blank" rel="external">http://www.web-polygraph.org/</a></p>
<p><a href="http://nginx.org/" target="_blank" rel="external">http://nginx.org/</a></p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/36. nginx 代理多台机器导致用户登陆异常，长连接(会话保持)解决问题/">nginx 代理多台机器导致用户登陆异常，长连接(会话保持)解决问题</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;网站使用程序 discuzx3  访问都正常，只有用户登陆存在异常，具体的情况是这样的：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;用户登陆后，会马上显示未登陆，然后刷新一下或者多下又变成了登陆中。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这个问题很显然是由于session导致，后台有多个web机器，当用户登陆后，会把登陆态session保存到当前web,但是再次发送请求时则会到另一台机器，所以原来的session信息找不到了。解决这个问题有两个思路：</p>
<ol>
<li><p>可以把session时时同步到另外的机器。</p>
</li>
<li><p>可以让前端的调度器保持长连接，也就是说某个用户的请求在某一时间段内始终抓发到固定的一台机器上。</p>
</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这两种方式，第二种更容易实现。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;我使用的是nginx的代理，其中nginx有一种算法支持长连接，具体配置是这样的：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">upstream <span class="built_in">test</span> &#123;</div><div class="line"> ip_hash;</div><div class="line">server 192.168.109.5;</div><div class="line">server 192.168.109.3;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;关键代码： ip_hash </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">E = mc^2</div></pre></td></tr></table></figure></p>
<p>这样，nginx会把用户的请求一直转发到后端的某台机器。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/34. Nginx的代理/">Nginx的代理</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div></pre></td><td class="code"><pre><div class="line">upstream bbs.aaa.cn&#123;</div><div class="line">        server 1.2.3.1:80;</div><div class="line">        server 1.2.3.4:80;</div><div class="line">    &#125;</div><div class="line"></div><div class="line"></div><div class="line">    server &#123;</div><div class="line">        listen 80;</div><div class="line">        server_name bbs.aaa.cn;</div><div class="line"></div><div class="line">        location / &#123;</div><div class="line">            proxy_pass http://bbs.aaa.cn/;</div><div class="line">            proxy_set_header Host <span class="variable">$host</span>;</div><div class="line">            proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</div><div class="line">            proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</div><div class="line">        &#125;</div><div class="line"><span class="comment">#         access_log /home/logs/bbs.access combined;</span></div><div class="line">    &#125;</div><div class="line"></div><div class="line"></div><div class="line">    upstream blog.aaa.cn&#123;</div><div class="line">        server 1.2.3.1:80;</div><div class="line">        server 1.2.3.4:80;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    server &#123;</div><div class="line">        listen 80;</div><div class="line">        server_name blog.aaa.cn;</div><div class="line"></div><div class="line">        location / &#123;</div><div class="line">            proxy_pass http://blog.aaa.cn/;</div><div class="line">            proxy_set_header Host <span class="variable">$host</span>;</div><div class="line">            proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</div><div class="line">            proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</div><div class="line">        &#125;</div><div class="line"><span class="comment">#         access_log /home/logs/ss.access combined;</span></div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;经测试发现，可以用nginx代理一个服务器上所有域名，方法如下：</p>
<ol>
<li><p>主配置文件不需要更改任何配置</p>
</li>
<li><p>在vhosts目录下需要建立两个文件，一个是servername 列表文件，一个是虚拟主机配置文件</p>
</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;两个文件内容分别为</p>
<ul>
<li>servername</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">server_name www.123.net.cn www.alsdjfl.com www.asdfa1.com;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;就这么简单一行，当然这个server_name 还可以继续添加的</p>
<ul>
<li>虚拟主机配置文件</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">server &#123;</div><div class="line">    listen 80;</div><div class="line">    include vhosts/servername; <span class="comment"># 这里的文件就是上边那个servername列表文件</span></div><div class="line">    location / &#123;</div><div class="line">        proxy_pass http://1.2.1.2/; <span class="comment">#这里就是需要做代理的服务器ip地址了</span></div><div class="line">        proxy_set_header Host <span class="variable">$host</span>;</div><div class="line">        proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</div><div class="line">        proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</div><div class="line">    &#125;</div><div class="line">    access_log /dev/null; </div><div class="line">&#125;</div></pre></td></tr></table></figure>
	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/33. Nginx、LVS及HAProxy负载均衡软件的优缺点详解/">Nginx、LVS及HAProxy负载均衡软件的优缺点详解</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/LVS/">LVS</a> <a class="article__tag-link" href="/tags/Nginx/">Nginx</a> <a class="article__tag-link" href="/tags/haproxy/">haproxy</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件，本人都在多个项目中实施过，参考了一些资料，结合自己的一些使用经验，总结一下。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;一般对负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术。具体的应用需求还得具体分析，如果是中小型的Web应用，比如日PV小于1000万，用Nginx就完全可以了；如果机器不少，可以用DNS轮询，LVS所耗费的机器还是比较多的；大型网站或重要的服务，且服务器比较多时，可以考虑用LVS。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;一种是通过硬件来进行，常见的硬件有比较昂贵的F5和Array等商用的负载均衡器，它的优点就是有专业的维护团队来对这些服务进行维护、缺点就是花销太大，所以对于规模较小的网络服务来说暂时还没有需要使用；另外一种就是类似于Nginx/LVS/HAProxy的基于 Linux的开源免费的负载均衡软件，这些都是通过软件级别来实现，所以费用非常低廉。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;目前关于网站架构一般比较合理流行的架构方案：Web前端采用Nginx/HAProxy+ Keepalived作负载均衡器；后端采用 MySQL数据库一主多从和读写分离，采用LVS+Keepalived的架构。当然要根据项目具体需求制定方案。    </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;下面说说各自的特点和适用场合。</p>
<h2 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h2><h3 id="Nginx的优点是："><a href="#Nginx的优点是：" class="headerlink" title="Nginx的优点是："></a>Nginx的优点是：</h3><ol>
<li><p>工作在网络的7层之上，可以针对http应用做一些分流的策略，比如针对域名、目录结构，它的正则规则比HAProxy更为强大和灵活，这也是它目前广泛流行的主要原因之一，Nginx单凭这点可利用的场合就远多于LVS了。 </p>
</li>
<li><p>Nginx对网络稳定性的依赖非常小，理论上能ping通就就能进行负载功能，这个也是它的优势之一；相反LVS对网络稳定性依赖比较大，这点本人深有体会； </p>
</li>
<li><p>Nginx安装和配置比较简单，测试起来比较方便，它基本能把错误用日志打印出来。LVS的配置、测试就要花比较长的时间了，LVS对网络依赖比较大。 </p>
</li>
<li><p>可以承担高负载压力且稳定，在硬件不差的情况下一般能支撑几万次的并发量，负载度比LVS相对小些。 </p>
</li>
<li><p>Nginx可以通过端口检测到服务器内部的故障，比如根据服务器处理网页返回的状态码、超时等等，并且会把返回错误的请求重新提交到另一个节点，不过其中缺点就是不支持url来检测。比如用户正在上传一个文件，而处理该上传的节点刚好在上传过程中出现故障，Nginx会把上传切到另一台服务器重新处理，而LVS就直接断掉了，如果是上传一个很大的文件或者很重要的文件的话，用户可能会因此而不满。 </p>
</li>
<li><p>Nginx不仅仅是一款优秀的负载均衡器/反向代理软件，它同时也是功能强大的Web应用服务器。LNMP也是近几年非常流行的web架构，在高流量的环境中稳定性也很好。 </p>
</li>
<li><p>Nginx现在作为Web反向加速缓存越来越成熟了，速度比传统的Squid服务器更快，可以考虑用其作为反向代理加速器。 </p>
</li>
<li><p>Nginx可作为中层反向代理使用，这一层面Nginx基本上无对手，唯一可以对比Nginx的就只有 lighttpd了，不过 lighttpd目前还没有做到Nginx完全的功能，配置也不那么清晰易读，社区资料也远远没Nginx活跃。 </p>
</li>
<li><p>Nginx也可作为静态网页和图片服务器，这方面的性能也无对手。还有Nginx社区非常活跃，第三方模块也很多。</p>
</li>
</ol>
<h3 id="Nginx的缺点是："><a href="#Nginx的缺点是：" class="headerlink" title="Nginx的缺点是："></a>Nginx的缺点是：</h3><ol>
<li>Nginx仅能支持http、https和Email协议，这样就在适用范围上面小些，这个是它的缺点。 </li>
<li><strong>对后端服务器的健康检查，只支持通过端口来检测，不支持通过url来检测</strong>。不支持Session的直接保持，但能通过ip_hash来解决。</li>
</ol>
<h2 id="LVS"><a href="#LVS" class="headerlink" title="LVS"></a>LVS</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;LVS：使用Linux内核集群实现一个高性能、高可用的负载均衡服务器，它具有很好的可伸缩性（Scalability)、可靠性（Reliability)和可管理性（Manageability)。</p>
<h3 id="LVS的优点是："><a href="#LVS的优点是：" class="headerlink" title="LVS的优点是："></a>LVS的优点是：</h3><ol>
<li><p>抗负载能力强、是工作在网络4层之上仅作分发之用，没有流量的产生，这个特点也决定了它在负载均衡软件里的性能最强的，对内存和cpu资源消耗比较低。 </p>
</li>
<li><p>配置性比较低，这是一个缺点也是一个优点，因为没有可太多配置的东西，所以并不需要太多接触，大大减少了人为出错的几率。 </p>
</li>
<li><p>工作稳定，因为其本身抗负载能力很强，自身有完整的双机热备方案，如LVS+Keepalived，不过我们在项目实施中用得最多的还是LVS/DR+Keepalived。 </p>
</li>
<li><p>无流量，LVS只分发请求，而流量并不从它本身出去，这点保证了均衡器IO的性能不会受到大流量的影响。 </p>
</li>
<li><p>应用范围比较广，因为LVS工作在4层，所以它几乎可以对所有应用做负载均衡，包括http、数据库、在线聊天室等等。</p>
</li>
</ol>
<h3 id="LVS的缺点是："><a href="#LVS的缺点是：" class="headerlink" title="LVS的缺点是："></a>LVS的缺点是：</h3><ol>
<li><p>软件本身不支持正则表达式处理，不能做动静分离；而现在许多网站在这方面都有较强的需求，这个是Nginx/HAProxy+Keepalived的优势所在。 </p>
</li>
<li><p>如果是网站应用比较庞大的话，LVS/DR+Keepalived实施起来就比较复杂了，特别后面有 Windows Server的机器的话，如果实施及配置还有维护过程就比较复杂了，相对而言，Nginx/HAProxy+Keepalived就简单多了。</p>
</li>
</ol>
<h2 id="HAProxy"><a href="#HAProxy" class="headerlink" title="HAProxy"></a>HAProxy</h2><h3 id="HAProxy的特点是："><a href="#HAProxy的特点是：" class="headerlink" title="HAProxy的特点是："></a>HAProxy的特点是：</h3><ol>
<li><p>HAProxy也是支持虚拟主机的。 </p>
</li>
<li><p>HAProxy的优点能够补充Nginx的一些缺点，比如支持Session的保持，Cookie的引导；同时支持通过获取指定的url来检测后端服务器的状态。 </p>
</li>
<li><p>HAProxy跟LVS类似，本身就只是一款负载均衡软件；单纯从效率上来讲HAProxy会比Nginx有更出色的负载均衡速度，在并发处理上也是优于Nginx的。 </p>
</li>
<li><p>HAProxy支持TCP协议的负载均衡转发，可以对MySQL读进行负载均衡，对后端的MySQL节点进行检测和负载均衡，大家可以用LVS+Keepalived对MySQL主从做负载均衡。 </p>
</li>
<li><p>HAProxy负载均衡策略非常多，HAProxy的负载均衡算法现在具体有如下8种： </p>
</li>
</ol>
<ul>
<li>roundrobin，表示简单的轮询，这个不多说，这个是负载均衡基本都具备的； </li>
<li>static-rr，表示根据权重，建议关注； </li>
<li>leastconn，表示最少连接者先处理，建议关注； </li>
<li>source，表示根据请求源IP，这个跟Nginx的IP_hash机制类似，我们用其作为解决session问题的一种方法，建议关注； </li>
<li>ri，表示根据请求的URI； </li>
<li>rl_param，表示根据请求的URl参数’balance url_param’ requires an URL parameter name； </li>
<li>hdr(name)，表示根据HTTP请求头来锁定每一次HTTP请求； </li>
<li>rdp-cookie(name)，表示根据据cookie(name)来锁定并哈希每一次TCP请求。</li>
</ul>
<h2 id="Nginx和LVS对比的总结："><a href="#Nginx和LVS对比的总结：" class="headerlink" title="Nginx和LVS对比的总结："></a>Nginx和LVS对比的总结：</h2><ol>
<li><p>Nginx工作在网络的7层，所以它可以针对http应用本身来做分流策略，比如针对域名、目录结构等，相比之下LVS并不具备这样的功能，所以Nginx单凭这点可利用的场合就远多于LVS了；但Nginx有用的这些功能使其可调整度要高于LVS，所以经常要去触碰触碰，触碰多了，人为出问题的几率也就会大。    </p>
</li>
<li><p>Nginx对网络稳定性的依赖较小，理论上只要ping得通，网页访问正常，Nginx就能连得通，这是Nginx的一大优势！Nginx同时还能区分内外网，如果是同时拥有内外网的节点，就相当于单机拥有了备份线路；LVS就比较依赖于网络环境，目前来看服务器在同一网段内并且LVS使用direct方式分流，效果较能得到保证。另外注意，LVS需要向托管商至少申请多一个ip来做Visual IP，貌似是不能用本身的IP来做VIP的。要做好LVS管理员，确实得跟进学习很多有关网络通信方面的知识，就不再是一个HTTP那么简单了。</p>
</li>
<li><p>Nginx安装和配置比较简单，测试起来也很方便，因为它基本能把错误用日志打印出来。LVS的安装和配置、测试就要花比较长的时间了；LVS对网络依赖比较大，很多时候不能配置成功都是因为网络问题而不是配置问题，出了问题要解决也相应的会麻烦得多。 </p>
</li>
<li><p>Nginx也同样能承受很高负载且稳定，但负载度和稳定度差LVS还有几个等级：Nginx处理所有流量所以受限于机器IO和配置；本身的bug也还是难以避免的。 </p>
</li>
<li><p>Nginx可以检测到服务器内部的故障，比如根据服务器处理网页返回的状态码、超时等等，并且会把返回错误的请求重新提交到另一个节点。目前LVS中 ldirectd也能支持针对服务器内部的情况来监控，但LVS的原理使其不能重发请求。比如用户正在上传一个文件，而处理该上传的节点刚好在上传过程中出现故障，Nginx会把上传切到另一台服务器重新处理，而LVS就直接断掉了，如果是上传一个很大的文件或者很重要的文件的话，用户可能会因此而恼火。 </p>
</li>
<li><p>Nginx对请求的异步处理可以帮助节点服务器减轻负载，假如使用 apache直接对外服务，那么出现很多的窄带链接时apache服务器将会占用大 量内存而不能释放，使用多一个Nginx做apache代理的话，这些窄带链接会被Nginx挡住，apache上就不会堆积过多的请求，这样就减少了相当多的资源占用。这点使用squid也有相同的作用，即使squid本身配置为不缓存，对apache还是有很大帮助的。 </p>
</li>
<li><p>Nginx能支持http、https和email（email的功能比较少用），LVS所支持的应用在这点上会比Nginx更多。在使用上，一般最前端所采取的策略应是LVS，也就是DNS的指向应为LVS均衡器，LVS的优点令它非常适合做这个任务。重要的ip地址，最好交由LVS托管，比如数据库的 ip、webservice服务器的ip等等，这些ip地址随着时间推移，使用面会越来越大，如果更换ip则故障会接踵而至。所以将这些重要ip交给 LVS托管是最为稳妥的，这样做的唯一缺点是需要的VIP数量会比较多。Nginx可作为LVS节点机器使用，一是可以利用Nginx的功能，二是可以利用Nginx的性能。当然这一层面也可以直接使用squid，squid的功能方面就比Nginx弱不少了，性能上也有所逊色于Nginx。Nginx也可作为中层代理使用，这一层面Nginx基本上无对手，唯一可以撼动Nginx的就只有lighttpd了，不过lighttpd目前还没有能做到 Nginx完全的功能，配置也不那么清晰易读。另外，中层代理的IP也是重要的，所以中层代理也拥有一个VIP和LVS是最完美的方案了。具体的应用还得具体分析，如果是比较小的网站（日PV小于1000万），用Nginx就完全可以了，如果机器也不少，可以用DNS轮询，LVS所耗费的机器还是比较多的；大型网站或者重要的服务，机器不发愁的时候，要多多考虑利用LVS。</p>
</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;现在对网络负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术：</p>
<ul>
<li><p>第一阶段：利用Nginx或HAProxy进行单点的负载均衡，这一阶段服务器规模刚脱离开单服务器、单数据库的模式，需要一定的负载均衡，但是仍然规模较小没有专业的维护团队来进行维护，也没有需要进行大规模的网站部署。这样利用Nginx或HAproxy就是第一选择，此时这些东西上手快， 配置容易，在七层之上利用HTTP协议就可以。这时是第一选择。</p>
</li>
<li><p>第二阶段：随着网络服务进一步扩大，这时单点的Nginx已经不能满足，这时使用LVS或者商用Array就是首要选择，Nginx此时就作为LVS或者Array的节点来使用，具体LVS或Array的是选择是根据公司规模和预算来选择，Array的应用交付功能非常强大，本人在某项目中使用过，性价比也远高于F5，商用首选，但是一般来说这阶段相关人才跟不上业务的提升，所以购买商业负载均衡已经成为了必经之路。</p>
</li>
<li><p>第三阶段：这时网络服务已经成为主流产品，此时随着公司知名度也进一步扩展，相关人才的能力以及数量也随之提升，这时无论从开发适合自身产品的定制，以及降低成本来讲开源的LVS，已经成为首选，这时LVS会成为主流。    </p>
</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;最终形成比较理想的基本架构为：Array/LVS — Nginx/Haproxy — Squid/Varnish — AppServer。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/32. nginx代理--根据访问的目录来区分后端的web/">nginx代理--根据访问的目录来区分后端的web</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;需求： 当请求的目录是 /aaa/ 则把请求发送到机器a，当请求的目录为/bbb/则把请求发送到机器b，除了目录/aaa/与目录/bbb/外，其他的请求发送到机器b</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;配置文件内容为：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line">upstream aaa.com</div><div class="line">&#123;</div><div class="line">        server 192.168.111.6;</div><div class="line">&#125;</div><div class="line">        upstream bbb.com</div><div class="line">&#123;</div><div class="line">        server 192.168.111.20;</div><div class="line">&#125;</div><div class="line">server &#123;</div><div class="line">        listen 80;</div><div class="line">        server_name li.com;</div><div class="line">        location /aaa/</div><div class="line">        &#123;</div><div class="line">        proxy_pass http://aaa.com/aaa/;</div><div class="line">        proxy_set_header Host <span class="variable">$host</span>;</div><div class="line">        proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</div><div class="line">        proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</div><div class="line">        &#125;</div><div class="line">location /bbb/</div><div class="line">        &#123;</div><div class="line">        proxy_pass http://bbb.com/bbb/;</div><div class="line">        proxy_set_header Host <span class="variable">$host</span>;</div><div class="line">        proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</div><div class="line">        proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</div><div class="line">        &#125;</div><div class="line">location /</div><div class="line">        &#123;</div><div class="line">        proxy_pass http://bbb.com/;</div><div class="line">        proxy_set_header Host <span class="variable">$host</span>;</div><div class="line">        proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</div><div class="line">        proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</div><div class="line">        &#125;</div><div class="line">       &#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;说明：</p>
<ol>
<li><p>以上配置文件中的 aaa.com 以及 bbb.com 都是自定义的，随便写。</p>
</li>
<li><p>upstream 中的server 可以写多个，例如</p>
</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">upstream aaa.com </div><div class="line">&#123;</div><div class="line">    server 192.168.111.6;</div><div class="line">    server 192.168.111.4;</div><div class="line">    server 192.168.111.5;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ol>
<li><p>proxy_pass <a href="http://aaa.com/aaa/" target="_blank" rel="external">http://aaa.com/aaa/</a>  这里必须要加这个目录，不然就访问到根目录了。</p>
</li>
<li><p>实际上，上述配置文件中， localtion /bbb/ 部分是可以省略掉的，因为后边的 location /  已经包含了/bbb/，所以即使我们不去定义  localtion /bbb/ 也是会访问到 bbb.com 的。</p>
</li>
</ol>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Nginx/31. nginx的负载均衡集群/">nginx的负载均衡和反向代理</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;之前提到过的 nginx 的负载均衡功能，实际上和 nginx 的代理是同一个功能，只是把之前代理一台机器改为代理多台机器而已。 nginx 的负载均衡和 lvs 相比， nginx 属于更该机的应用层，不牵扯到 IP 和内核的改动， 它只是单纯地把用户的请求转发到后面的机器上，这就意味着，后端的 RS 不需要配置公网 IP。</p>
<h2 id="1-环境说明"><a href="#1-环境说明" class="headerlink" title="1.环境说明"></a>1.环境说明</h2><ul>
<li>nginx 分发器（一个公网 ip 192.168.119.110 和一个内网 ip 192.168.0.67）</li>
<li>RS1 只有内网 ip（192.168.0.66）</li>
<li>RS2 只有内网 ip（192.168.0.65）</li>
</ul>
<h2 id="2-配置"><a href="#2-配置" class="headerlink" title="2.配置"></a>2.配置</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在 nginx 分发器上编辑配置文件</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@dir ~]<span class="comment"># vim /usr/local/nginx/conf/vhosts/lb.conf</span></div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;加入内容</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">upstream <span class="built_in">test</span> &#123;</div><div class="line">    ip_hash;</div><div class="line">    server 192.168.0.66;</div><div class="line">    server 192.168.0.65;</div><div class="line">&#125;</div><div class="line">    server &#123;</div><div class="line">        listen 80;</div><div class="line">        server_name bbs.aaa.cn;</div><div class="line">        location / &#123;</div><div class="line">            proxy_pass http://<span class="built_in">test</span>/;</div><div class="line">            proxy_set_header Host <span class="variable">$host</span>;</div><div class="line">            proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</div><div class="line">            proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;说明：这个配置和 nginx 代理配置如出一辙，只是多了一个 upstream ，这个 upstream 用来定义后端的 RS ，可以只写一个。 ip_hash 为 nginx 的一种调度算法，加上这一行会达到这样的效果，即一个用户的请求会适中被分发到固定的一个 RS 上。这样的好处是，可以避免同一个用户的请求分发到不同的机器上而导致 session 丢失的情况。 upstream 里面，RS 后面的 ip 后面还可以加权重，比如 “server 192.168.0.66 weight=100;”。还有一点要注意， upstream 后面的 test 是自定义的一个名字，可以随便写，唯一的要求是要和 proxy_pass 后面保持一致。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;针对目录参考<a href="https://hcldirgit.github.io/2017/08/08/32.%20nginx%E4%BB%A3%E7%90%86--%E6%A0%B9%E6%8D%AE%E8%AE%BF%E9%97%AE%E7%9A%84%E7%9B%AE%E5%BD%95%E6%9D%A5%E5%8C%BA%E5%88%86%E5%90%8E%E7%AB%AF%E7%9A%84web/" target="_blank" rel="external">nginx代理–根据访问的目录来区分后端的web</a></p>

	

	

</article>





	<span class="different-posts">📖 <a href="/page/24">more posts</a> 📖</span>



	</main>

	<footer class="footer">
	<div class="footer-content">
		
	      <div class="footer__element">
	<p>Hi there, <br />welcome to my Blog glad you found it. Have a look around, will you?</p>
</div>

	    
	      <div class="footer__element">
	<h5>Check out</h5>
	<ul class="footer-links">
		<li class="footer-links__link"><a href="/archives">Archive</a></li>
		
		  <li class="footer-links__link"><a href="/atom.xml">RSS</a></li>
	    
		<li class="footer-links__link"><a href="/about">about page</a></li>
		<li class="footer-links__link"><a href="/tags">Tags</a></li>
		<li class="footer-links__link"><a href="/categories">Categories</a></li>
	</ul>
</div>

	    

		<div class="footer-credit">
			<span>© 2017 失落的乐章 | Powered by <a href="https://hexo.io/">Hexo</a> | Theme <a href="https://github.com/HoverBaum/meilidu-hexo">MeiliDu</a></span>
		</div>

	</div>


</footer>



</body>

</html>
